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.
- package/README.md +82 -46
- package/dist/index.js +114 -2
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -1,72 +1,108 @@
|
|
|
1
1
|
# think
|
|
2
2
|
|
|
3
|
-
Local-first CLI
|
|
3
|
+
Local-first CLI that gives AI agents persistent, curated memory.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Install
|
|
6
6
|
|
|
7
|
-
Requires **Node
|
|
7
|
+
Requires **Node 22.5+** (uses `node:sqlite`).
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
|
|
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
|
-
|
|
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
|
-
|
|
24
|
-
|
|
16
|
+
# Log work events
|
|
17
|
+
think sync "shipped the auth fix"
|
|
18
|
+
think sync "EEP prototype demoed to product team"
|
|
25
19
|
|
|
26
|
-
|
|
20
|
+
# List recent entries
|
|
21
|
+
think list --week
|
|
27
22
|
|
|
28
|
-
-
|
|
29
|
-
|
|
23
|
+
# AI-powered summary
|
|
24
|
+
think summary
|
|
25
|
+
think summary --last-week --raw # raw entries, no AI
|
|
26
|
+
```
|
|
30
27
|
|
|
31
|
-
|
|
28
|
+
## Cortex — shared team memory
|
|
32
29
|
|
|
33
|
-
|
|
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
|
-
#
|
|
37
|
-
think
|
|
38
|
-
think
|
|
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
|
-
#
|
|
42
|
-
think
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
#
|
|
47
|
-
think
|
|
48
|
-
|
|
51
|
+
# Pull another team's memories
|
|
52
|
+
think pull product
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Privacy
|
|
49
56
|
|
|
50
|
-
|
|
51
|
-
think
|
|
52
|
-
think
|
|
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
|
-
|
|
62
|
+
### Curator guidance
|
|
57
63
|
|
|
58
|
-
|
|
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
|
-
|
|
66
|
+
```bash
|
|
67
|
+
think curator edit # opens ~/.think/curator.md in $EDITOR
|
|
68
|
+
think curator show # print current guidance
|
|
69
|
+
```
|
|
65
70
|
|
|
66
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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.
|
|
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",
|