open-think 0.1.0 → 0.1.2

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 (3) hide show
  1. package/README.md +82 -46
  2. package/dist/index.js +114 -2
  3. package/package.json +4 -1
package/README.md CHANGED
@@ -1,72 +1,108 @@
1
1
  # think
2
2
 
3
- Local-first CLI for capturing notes, work logs, and ideas with P2P sync between machines.
3
+ Local-first CLI that gives AI agents persistent, curated memory.
4
4
 
5
- ## Setup
5
+ ## Install
6
6
 
7
- Requires **Node 20** (native SQLite extensions don't compile on Node 22+).
7
+ Requires **Node 22.5+** (uses `node:sqlite`).
8
8
 
9
9
  ```bash
10
- git clone git@github.com:MicroMediaSites/think-cli.git
11
- cd think-cli
12
- nvm use 20
13
- npm install
14
- npm run build
15
- npm link
10
+ npm install -g open-think
16
11
  ```
17
12
 
18
- After `npm link`, the `think` command is available globally.
19
-
20
- **Note:** The build pins the Node 20 binary path in the shebang, so `think` will always use Node 20 regardless of your active nvm version. If your Node 20 is at a different path than the machine that last ran `npm run build`, just rebuild:
13
+ ## Quick start
21
14
 
22
15
  ```bash
23
- nvm use 20 && npm run build
24
- ```
16
+ # Log work events
17
+ think sync "shipped the auth fix"
18
+ think sync "EEP prototype demoed to product team"
25
19
 
26
- ## Data
20
+ # List recent entries
21
+ think list --week
27
22
 
28
- - **Database:** `~/.local/share/think/think.db`
29
- - **Config:** `~/.config/think/config.json` (auto-generated on first run)
23
+ # AI-powered summary
24
+ think summary
25
+ think summary --last-week --raw # raw entries, no AI
26
+ ```
30
27
 
31
- Data lives on disk, not in the repo. Use `think network sync` to replicate between machines.
28
+ ## Cortex shared team memory
32
29
 
33
- ## Usage
30
+ Cortexes connect agents across a team via a shared git repo. Engrams (raw events) stay local. A curator agent evaluates them and appends curated memories to the repo.
34
31
 
35
32
  ```bash
36
- # Log entries
37
- think log "idea for caching layer"
38
- think log "standup — discussed deploy timeline" --category meeting
39
- think sync "shipped the auth fix" # shorthand for --category sync
33
+ # Set up (once)
34
+ think cortex setup git@github.com:org/hivedb.git
35
+ think cortex create engineering
40
36
 
41
- # List entries
42
- think list --week
43
- think list --last-week --category sync
44
- think list --tag architecture
37
+ # Work normally — think sync logs engrams locally
38
+ think sync "deployed auth service to staging"
39
+
40
+ # Curate evaluate engrams, append memories to the branch
41
+ think curate # full run
42
+ think curate --dry-run # preview without pushing
43
+
44
+ # Read team memories
45
+ think recall "auth" # search memories + local engrams
46
+ think memory # show all memories from branch
47
+
48
+ # Monitor curation quality
49
+ think monitor # what got promoted vs dropped
45
50
 
46
- # Weekly summary (uses Claude subscription via Agent SDK)
47
- think summary # AI-powered summary of current week
48
- think summary --last-week --raw # raw entries, no AI
51
+ # Pull another team's memories
52
+ think pull product
53
+ ```
54
+
55
+ ### Privacy
49
56
 
50
- # P2P sync (both machines must be on the same LAN)
51
- think network sync # discover peers via mDNS and sync
52
- think network sync --host 192.168.1.50 # sync with a specific machine
53
- think network status # show known peers and last sync times
57
+ ```bash
58
+ think pause # suppress engram creation (silent no-op)
59
+ think resume # re-enable
54
60
  ```
55
61
 
56
- ## Categories
62
+ ### Curator guidance
57
63
 
58
- - `note` (default) general notes and ideas
59
- - `sync` — work log entries for 1:1 meetings
60
- - `meeting` — meeting notes
61
- - `decision` — decisions made
62
- - `idea` — ideas to revisit
64
+ Each contributor can guide their curator with a personal prompt:
63
65
 
64
- ## Syncing between machines
66
+ ```bash
67
+ think curator edit # opens ~/.think/curator.md in $EDITOR
68
+ think curator show # print current guidance
69
+ ```
65
70
 
66
- Both machines need `think` installed. When on the same network:
71
+ ## Data
72
+
73
+ - **Engrams:** `~/.local/share/think/think.db` (no cortex) or `~/.think/engrams/<cortex>.db`
74
+ - **Config:** `~/.config/think/config.json`
75
+ - **Curator guidance:** `~/.think/curator.md`
76
+ - **Memories:** `memories.jsonl` on cortex git branches (append-only JSONL)
67
77
 
68
- 1. Run `think network sync` on either machine
69
- 2. Peers discover each other automatically via mDNS/Bonjour
70
- 3. Entries replicate in both directions using CRDTs (no conflicts)
78
+ ## All commands
71
79
 
72
- After syncing, `think summary --week` on either machine produces the same output.
80
+ ```
81
+ think sync <message> Log a work event
82
+ think log <message> Log a note (with --category, --tags)
83
+ think list List entries (--week, --since, --category)
84
+ think summary AI summary (--raw for plain text)
85
+ think delete Soft-delete entries
86
+
87
+ think cortex setup <repo> Configure git repo for shared memory
88
+ think cortex create <name> Create a cortex branch
89
+ think cortex list Show cortex branches
90
+ think cortex switch <name> Set active cortex
91
+ think cortex current Show active cortex
92
+
93
+ think curate Run curation (--dry-run to preview)
94
+ think monitor Show promoted vs dropped engrams
95
+ think recall <query> Search memories + engrams
96
+ think memory Show memories (--history for git log)
97
+ think pull <cortex> Pull another cortex's memories
98
+
99
+ think curator edit Edit personal curator guidance
100
+ think curator show Show current guidance
101
+ think pause Suppress engram creation
102
+ think resume Re-enable engram creation
103
+
104
+ think init Set up CLAUDE.md for auto-logging
105
+ think export Export entries as sync bundle
106
+ think import <file> Import sync bundle
107
+ think audit Show sync audit log
108
+ ```
package/dist/index.js CHANGED
@@ -1,10 +1,11 @@
1
1
  #!/usr/bin/env node --no-warnings=ExperimentalWarning
2
2
 
3
3
  // src/index.ts
4
- import { Command as Command16 } from "commander";
4
+ import { Command as Command18 } from "commander";
5
5
 
6
6
  // src/commands/log.ts
7
7
  import { Command } from "commander";
8
+ import { spawn } from "child_process";
8
9
  import chalk from "chalk";
9
10
 
10
11
  // src/db/queries.ts
@@ -346,6 +347,9 @@ var logCommand = new Command("log").description("Log a note or entry").argument(
346
347
  var syncCommand = new Command("sync").description("Log a sync/work-log entry (shorthand for log --category sync)").argument("<message>", "The message to log").option("-s, --source <source>", "Source of the entry", "manual").option("-t, --tags <tags>", "Comma-separated tags").option("--silent", "Suppress output").action(function(message, opts) {
347
348
  const globalOpts = this.optsWithGlobals();
348
349
  const config = getConfig();
350
+ if (config.paused) {
351
+ return;
352
+ }
349
353
  const cortex = globalOpts.cortex ?? config.cortex?.active;
350
354
  if (cortex) {
351
355
  const engram = insertEngram(cortex, { content: message });
@@ -355,6 +359,18 @@ var syncCommand = new Command("sync").description("Log a sync/work-log entry (sh
355
359
  console.log(`${chalk.green("\u2713")} ${badge} engram saved ${ts}`);
356
360
  console.log(` ${engram.content}`);
357
361
  }
362
+ const curateEveryN = config.cortex?.curateEveryN;
363
+ if (curateEveryN && curateEveryN > 0) {
364
+ const pending = getPendingEngrams(cortex);
365
+ if (pending.length >= curateEveryN) {
366
+ if (!opts.silent) {
367
+ console.log(chalk.dim(` ${pending.length} pending engrams \u2014 triggering curation...`));
368
+ }
369
+ closeEngramsDb(cortex);
370
+ spawn(process.execPath, [process.argv[1], "curate"], { detached: true, stdio: "ignore" }).unref();
371
+ return;
372
+ }
373
+ }
358
374
  closeEngramsDb(cortex);
359
375
  } else {
360
376
  const tags = opts.tags ? opts.tags.split(",").map((t) => t.trim()) : void 0;
@@ -1019,6 +1035,7 @@ cortexCommand.addCommand(new Command9("current").description("Show the active co
1019
1035
 
1020
1036
  // src/commands/curate.ts
1021
1037
  import { Command as Command10 } from "commander";
1038
+ import readline3 from "readline";
1022
1039
  import chalk10 from "chalk";
1023
1040
 
1024
1041
  // src/lib/curator.ts
@@ -1214,6 +1231,42 @@ var curateCommand = new Command10("curate").description("Run curation: evaluate
1214
1231
  closeEngramsDb(cortex);
1215
1232
  return;
1216
1233
  }
1234
+ if (config.cortex?.confirmBeforeCommit && newEntries.length > 0) {
1235
+ console.log();
1236
+ console.log(chalk10.cyan("Proposed memories:"));
1237
+ for (let i = 0; i < newEntries.length; i++) {
1238
+ console.log(chalk10.green(` ${i + 1}. `) + newEntries[i].content);
1239
+ }
1240
+ console.log();
1241
+ const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
1242
+ const answer = await new Promise((resolve) => {
1243
+ rl.question(" Commit these memories? [Y/n/edit] ", (ans) => {
1244
+ rl.close();
1245
+ resolve(ans.trim().toLowerCase());
1246
+ });
1247
+ });
1248
+ if (answer === "n" || answer === "no") {
1249
+ console.log(chalk10.dim(" Aborted. Engrams left as pending."));
1250
+ closeEngramsDb(cortex);
1251
+ return;
1252
+ }
1253
+ if (answer === "e" || answer === "edit") {
1254
+ for (let i = 0; i < newEntries.length; i++) {
1255
+ const editRl = readline3.createInterface({ input: process.stdin, output: process.stdout });
1256
+ const edited = await new Promise((resolve) => {
1257
+ editRl.question(` ${i + 1}. ${chalk10.dim("(enter to keep, or type replacement)")}
1258
+ ${newEntries[i].content}
1259
+ > `, (ans) => {
1260
+ editRl.close();
1261
+ resolve(ans.trim());
1262
+ });
1263
+ });
1264
+ if (edited) {
1265
+ newEntries[i].content = edited;
1266
+ }
1267
+ }
1268
+ }
1269
+ }
1217
1270
  if (newEntries.length > 0) {
1218
1271
  const newLines = newEntries.map((e) => JSON.stringify(e));
1219
1272
  const commitMsg = `curate: ${author}, ${pending.length} engrams, ${newEntries.length} memories`;
@@ -1444,8 +1497,64 @@ var pullCommand = new Command15("pull").argument("<cortex>", "Cortex branch to p
1444
1497
  ${recentMemories.length} memories`));
1445
1498
  });
1446
1499
 
1500
+ // src/commands/pause.ts
1501
+ import { Command as Command16 } from "commander";
1502
+ import chalk16 from "chalk";
1503
+ var pauseCommand = new Command16("pause").description("Pause engram creation \u2014 think sync will silently skip until resumed").action(() => {
1504
+ const config = getConfig();
1505
+ config.paused = true;
1506
+ saveConfig(config);
1507
+ console.log(chalk16.yellow("\u23F8") + " Engram creation paused. Run " + chalk16.dim("think resume") + " to re-enable.");
1508
+ });
1509
+ var resumeCommand = new Command16("resume").description("Resume engram creation after a pause").action(() => {
1510
+ const config = getConfig();
1511
+ config.paused = false;
1512
+ saveConfig(config);
1513
+ console.log(chalk16.green("\u2713") + " Engram creation resumed.");
1514
+ });
1515
+
1516
+ // src/commands/config-cmd.ts
1517
+ import { Command as Command17 } from "commander";
1518
+ import chalk17 from "chalk";
1519
+ var ALLOWED_KEYS = /* @__PURE__ */ new Set([
1520
+ "cortex.curateEveryN",
1521
+ "cortex.confirmBeforeCommit",
1522
+ "cortex.author",
1523
+ "cortex.repo",
1524
+ "cortex.active",
1525
+ "paused"
1526
+ ]);
1527
+ var configCommand = new Command17("config").description("View or update think configuration");
1528
+ configCommand.addCommand(new Command17("show").description("Print current configuration").action(() => {
1529
+ const config = getConfig();
1530
+ console.log(JSON.stringify(config, null, 2));
1531
+ }));
1532
+ configCommand.addCommand(new Command17("set").argument("<key>", "Config key (e.g., cortex.curateEveryN, cortex.confirmBeforeCommit)").argument("<value>", "Value to set").description("Set a configuration value").action((key, value) => {
1533
+ if (!ALLOWED_KEYS.has(key)) {
1534
+ console.error(chalk17.red(`Unknown config key: ${key}`));
1535
+ console.error(chalk17.dim(`Allowed keys: ${[...ALLOWED_KEYS].join(", ")}`));
1536
+ process.exit(1);
1537
+ }
1538
+ const config = getConfig();
1539
+ let parsed = value;
1540
+ if (value === "true") parsed = true;
1541
+ else if (value === "false") parsed = false;
1542
+ else if (/^\d+$/.test(value)) parsed = parseInt(value, 10);
1543
+ const parts = key.split(".");
1544
+ let target = config;
1545
+ for (let i = 0; i < parts.length - 1; i++) {
1546
+ if (!target[parts[i]] || typeof target[parts[i]] !== "object") {
1547
+ target[parts[i]] = {};
1548
+ }
1549
+ target = target[parts[i]];
1550
+ }
1551
+ target[parts[parts.length - 1]] = parsed;
1552
+ saveConfig(config);
1553
+ console.log(chalk17.green("\u2713") + ` ${key} = ${JSON.stringify(parsed)}`);
1554
+ }));
1555
+
1447
1556
  // src/index.ts
1448
- var program = new Command16();
1557
+ var program = new Command18();
1449
1558
  program.name("think").description("Local-first CLI tool for capturing notes, work logs, and ideas").version("0.1.0").option("-C, --cortex <name>", "Use a specific cortex for this command");
1450
1559
  program.addCommand(logCommand);
1451
1560
  program.addCommand(syncCommand);
@@ -1463,4 +1572,7 @@ program.addCommand(recallCommand);
1463
1572
  program.addCommand(memoryCommand);
1464
1573
  program.addCommand(curatorCommand);
1465
1574
  program.addCommand(pullCommand);
1575
+ program.addCommand(pauseCommand);
1576
+ program.addCommand(resumeCommand);
1577
+ program.addCommand(configCommand);
1466
1578
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-think",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "type": "module",
5
5
  "description": "Local-first CLI that gives AI agents persistent, curated memory",
6
6
  "bin": {
@@ -28,6 +28,9 @@
28
28
  "curation"
29
29
  ],
30
30
  "license": "MIT",
31
+ "engines": {
32
+ "node": ">=22.5.0"
33
+ },
31
34
  "dependencies": {
32
35
  "@anthropic-ai/claude-agent-sdk": "^0.2.98",
33
36
  "chalk": "^5.4.1",