open-think 0.1.2 → 0.1.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.
- package/dist/index.js +134 -40
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node --no-warnings=ExperimentalWarning
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import { Command as
|
|
4
|
+
import { Command as Command19 } from "commander";
|
|
5
5
|
|
|
6
6
|
// src/commands/log.ts
|
|
7
7
|
import { Command } from "commander";
|
|
@@ -324,6 +324,70 @@ function searchEngrams(cortexName, query3, limit = 20) {
|
|
|
324
324
|
}
|
|
325
325
|
}
|
|
326
326
|
|
|
327
|
+
// src/lib/update-check.ts
|
|
328
|
+
import fs4 from "fs";
|
|
329
|
+
import path4 from "path";
|
|
330
|
+
import { execFile } from "child_process";
|
|
331
|
+
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
332
|
+
var PACKAGE_NAME = "open-think";
|
|
333
|
+
function cachePath() {
|
|
334
|
+
return path4.join(getConfigDir(), "version-cache.json");
|
|
335
|
+
}
|
|
336
|
+
function readCache() {
|
|
337
|
+
try {
|
|
338
|
+
const raw = fs4.readFileSync(cachePath(), "utf-8");
|
|
339
|
+
return JSON.parse(raw);
|
|
340
|
+
} catch {
|
|
341
|
+
return null;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
function writeCache(cache) {
|
|
345
|
+
const dir = getConfigDir();
|
|
346
|
+
fs4.mkdirSync(dir, { recursive: true });
|
|
347
|
+
fs4.writeFileSync(cachePath(), JSON.stringify(cache), "utf-8");
|
|
348
|
+
}
|
|
349
|
+
function getInstalledVersion() {
|
|
350
|
+
try {
|
|
351
|
+
const pkgPath = path4.join(import.meta.dirname, "..", "package.json");
|
|
352
|
+
const raw = fs4.readFileSync(pkgPath, "utf-8");
|
|
353
|
+
return JSON.parse(raw).version ?? null;
|
|
354
|
+
} catch {
|
|
355
|
+
return null;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
function isNewer(latest, current) {
|
|
359
|
+
const l = latest.split(".").map(Number);
|
|
360
|
+
const c = current.split(".").map(Number);
|
|
361
|
+
for (let i = 0; i < 3; i++) {
|
|
362
|
+
if ((l[i] ?? 0) > (c[i] ?? 0)) return true;
|
|
363
|
+
if ((l[i] ?? 0) < (c[i] ?? 0)) return false;
|
|
364
|
+
}
|
|
365
|
+
return false;
|
|
366
|
+
}
|
|
367
|
+
function checkForUpdate() {
|
|
368
|
+
const installed = getInstalledVersion();
|
|
369
|
+
if (!installed) return null;
|
|
370
|
+
const cache = readCache();
|
|
371
|
+
const now = Date.now();
|
|
372
|
+
if (cache && now - cache.checkedAt < CHECK_INTERVAL_MS) {
|
|
373
|
+
if (isNewer(cache.latest, installed)) {
|
|
374
|
+
return `open-think ${cache.latest} available (you have ${installed}). Run: npm update -g open-think`;
|
|
375
|
+
}
|
|
376
|
+
return null;
|
|
377
|
+
}
|
|
378
|
+
execFile("npm", ["view", PACKAGE_NAME, "version"], { timeout: 5e3 }, (err, stdout) => {
|
|
379
|
+
if (err) return;
|
|
380
|
+
const latest = stdout.trim();
|
|
381
|
+
if (latest) {
|
|
382
|
+
writeCache({ latest, checkedAt: Date.now() });
|
|
383
|
+
}
|
|
384
|
+
});
|
|
385
|
+
if (cache && isNewer(cache.latest, installed)) {
|
|
386
|
+
return `open-think ${cache.latest} available (you have ${installed}). Run: npm update -g open-think`;
|
|
387
|
+
}
|
|
388
|
+
return null;
|
|
389
|
+
}
|
|
390
|
+
|
|
327
391
|
// src/commands/log.ts
|
|
328
392
|
var logCommand = new Command("log").description("Log a note or entry").argument("<message>", "The message to log").option("-s, --source <source>", "Source of the entry", "manual").option("-c, --category <category>", "Category: note, sync, meeting, decision, idea", "note").option("-t, --tags <tags>", "Comma-separated tags").option("--silent", "Suppress output").action((message, opts) => {
|
|
329
393
|
const tags = opts.tags ? opts.tags.split(",").map((t) => t.trim()) : void 0;
|
|
@@ -391,6 +455,12 @@ var syncCommand = new Command("sync").description("Log a sync/work-log entry (sh
|
|
|
391
455
|
}
|
|
392
456
|
closeDb();
|
|
393
457
|
}
|
|
458
|
+
if (!opts.silent) {
|
|
459
|
+
const updateMsg = checkForUpdate();
|
|
460
|
+
if (updateMsg) {
|
|
461
|
+
console.log(chalk.yellow(` \u2139 ${updateMsg}`));
|
|
462
|
+
}
|
|
463
|
+
}
|
|
394
464
|
});
|
|
395
465
|
|
|
396
466
|
// src/commands/list.ts
|
|
@@ -574,7 +644,7 @@ var deleteCommand = new Command4("delete").description("Soft-delete entries (tom
|
|
|
574
644
|
|
|
575
645
|
// src/commands/export.ts
|
|
576
646
|
import { Command as Command5 } from "commander";
|
|
577
|
-
import
|
|
647
|
+
import fs5 from "fs";
|
|
578
648
|
import chalk5 from "chalk";
|
|
579
649
|
var exportCommand = new Command5("export").description("Export entries as a sync bundle (file-based sync)").option("-o, --output <file>", "Write to file instead of stdout").option("--since <date>", "Export entries since date (ISO or YYYY-MM-DD)").option("-n, --limit <n>", "Max entries to export (default: all)").action((opts) => {
|
|
580
650
|
const config = getConfig();
|
|
@@ -598,7 +668,7 @@ var exportCommand = new Command5("export").description("Export entries as a sync
|
|
|
598
668
|
};
|
|
599
669
|
const json = JSON.stringify(bundle, null, 2);
|
|
600
670
|
if (opts.output) {
|
|
601
|
-
|
|
671
|
+
fs5.writeFileSync(opts.output, json, "utf-8");
|
|
602
672
|
console.log(chalk5.green("\u2713") + ` Exported ${entries.length} entries to ${opts.output}`);
|
|
603
673
|
if (opts.since) {
|
|
604
674
|
console.log(chalk5.dim(` since: ${opts.since}`));
|
|
@@ -611,36 +681,36 @@ var exportCommand = new Command5("export").description("Export entries as a sync
|
|
|
611
681
|
|
|
612
682
|
// src/commands/import.ts
|
|
613
683
|
import { Command as Command6 } from "commander";
|
|
614
|
-
import
|
|
684
|
+
import fs7 from "fs";
|
|
615
685
|
import chalk6 from "chalk";
|
|
616
686
|
|
|
617
687
|
// src/lib/audit.ts
|
|
618
|
-
import
|
|
619
|
-
import
|
|
688
|
+
import fs6 from "fs";
|
|
689
|
+
import path5 from "path";
|
|
620
690
|
function auditLogPath() {
|
|
621
|
-
return
|
|
691
|
+
return path5.join(getDataDir(), "sync-audit.log");
|
|
622
692
|
}
|
|
623
693
|
function logAudit(entry) {
|
|
624
694
|
const line = JSON.stringify(entry) + "\n";
|
|
625
|
-
|
|
695
|
+
fs6.appendFileSync(auditLogPath(), line, "utf-8");
|
|
626
696
|
}
|
|
627
697
|
function readAuditLog() {
|
|
628
698
|
const logPath = auditLogPath();
|
|
629
|
-
if (!
|
|
630
|
-
const lines =
|
|
699
|
+
if (!fs6.existsSync(logPath)) return [];
|
|
700
|
+
const lines = fs6.readFileSync(logPath, "utf-8").trim().split("\n").filter(Boolean);
|
|
631
701
|
return lines.map((line) => JSON.parse(line));
|
|
632
702
|
}
|
|
633
703
|
|
|
634
704
|
// src/commands/import.ts
|
|
635
705
|
var importCommand = new Command6("import").description("Import a sync bundle from another device").argument("<file>", "Path to the sync bundle JSON file").action((file) => {
|
|
636
|
-
if (!
|
|
706
|
+
if (!fs7.existsSync(file)) {
|
|
637
707
|
console.error(chalk6.red(`File not found: ${file}`));
|
|
638
708
|
closeDb();
|
|
639
709
|
process.exit(1);
|
|
640
710
|
}
|
|
641
711
|
let bundle;
|
|
642
712
|
try {
|
|
643
|
-
const raw =
|
|
713
|
+
const raw = fs7.readFileSync(file, "utf-8");
|
|
644
714
|
bundle = JSON.parse(raw);
|
|
645
715
|
} catch {
|
|
646
716
|
console.error(chalk6.red("Failed to parse sync bundle \u2014 is this a valid JSON file?"));
|
|
@@ -714,8 +784,8 @@ var importCommand = new Command6("import").description("Import a sync bundle fro
|
|
|
714
784
|
|
|
715
785
|
// src/commands/init.ts
|
|
716
786
|
import { Command as Command7 } from "commander";
|
|
717
|
-
import
|
|
718
|
-
import
|
|
787
|
+
import fs8 from "fs";
|
|
788
|
+
import path6 from "path";
|
|
719
789
|
import readline from "readline";
|
|
720
790
|
import chalk7 from "chalk";
|
|
721
791
|
var CLAUDE_MD_SECTION = `# Work Logging
|
|
@@ -751,7 +821,7 @@ var initCommand = new Command7("init").description("Set up Claude Code integrati
|
|
|
751
821
|
const defaultDir = home;
|
|
752
822
|
let targetDir;
|
|
753
823
|
if (opts.dir) {
|
|
754
|
-
targetDir =
|
|
824
|
+
targetDir = path6.resolve(opts.dir);
|
|
755
825
|
} else if (opts.yes) {
|
|
756
826
|
targetDir = defaultDir;
|
|
757
827
|
} else {
|
|
@@ -760,25 +830,25 @@ var initCommand = new Command7("init").description("Set up Claude Code integrati
|
|
|
760
830
|
defaultDir
|
|
761
831
|
);
|
|
762
832
|
targetDir = targetDir.replace(/^~/, home);
|
|
763
|
-
targetDir =
|
|
833
|
+
targetDir = path6.resolve(targetDir);
|
|
764
834
|
}
|
|
765
|
-
if (!
|
|
835
|
+
if (!fs8.existsSync(targetDir)) {
|
|
766
836
|
console.error(chalk7.red(`Directory does not exist: ${targetDir}`));
|
|
767
837
|
process.exit(1);
|
|
768
838
|
}
|
|
769
|
-
const filePath =
|
|
770
|
-
const exists =
|
|
839
|
+
const filePath = path6.join(targetDir, "CLAUDE.md");
|
|
840
|
+
const exists = fs8.existsSync(filePath);
|
|
771
841
|
if (exists) {
|
|
772
|
-
const existing =
|
|
842
|
+
const existing = fs8.readFileSync(filePath, "utf-8");
|
|
773
843
|
if (existing.includes("think sync")) {
|
|
774
844
|
console.log(chalk7.dim("CLAUDE.md already contains think sync instructions. Nothing to do."));
|
|
775
845
|
return;
|
|
776
846
|
}
|
|
777
847
|
const separator = existing.endsWith("\n") ? "\n" : "\n\n";
|
|
778
|
-
|
|
848
|
+
fs8.writeFileSync(filePath, existing + separator + CLAUDE_MD_SECTION, "utf-8");
|
|
779
849
|
console.log(chalk7.green("\u2713") + ` Appended work logging instructions to ${filePath}`);
|
|
780
850
|
} else {
|
|
781
|
-
|
|
851
|
+
fs8.writeFileSync(filePath, CLAUDE_MD_SECTION, "utf-8");
|
|
782
852
|
console.log(chalk7.green("\u2713") + ` Created ${filePath} with work logging instructions`);
|
|
783
853
|
}
|
|
784
854
|
console.log(chalk7.dim(" Claude Code sessions under this directory will now auto-log with think sync."));
|
|
@@ -829,8 +899,8 @@ import readline2 from "readline";
|
|
|
829
899
|
|
|
830
900
|
// src/lib/git.ts
|
|
831
901
|
import { execFileSync } from "child_process";
|
|
832
|
-
import
|
|
833
|
-
import
|
|
902
|
+
import fs9 from "fs";
|
|
903
|
+
import path7 from "path";
|
|
834
904
|
function runGit(args, cwd) {
|
|
835
905
|
const repoPath = cwd ?? getRepoPath();
|
|
836
906
|
return execFileSync("git", args, {
|
|
@@ -845,14 +915,14 @@ function ensureRepoCloned() {
|
|
|
845
915
|
throw new Error("No cortex repo configured. Run: think cortex setup");
|
|
846
916
|
}
|
|
847
917
|
const repoPath = getRepoPath();
|
|
848
|
-
if (
|
|
918
|
+
if (fs9.existsSync(path7.join(repoPath, ".git"))) {
|
|
849
919
|
const remote = runGit(["remote", "get-url", "origin"], repoPath);
|
|
850
920
|
if (remote !== config.cortex.repo) {
|
|
851
921
|
throw new Error(`Repo at ${repoPath} points to ${remote}, expected ${config.cortex.repo}`);
|
|
852
922
|
}
|
|
853
923
|
return;
|
|
854
924
|
}
|
|
855
|
-
|
|
925
|
+
fs9.mkdirSync(repoPath, { recursive: true });
|
|
856
926
|
execFileSync("git", ["clone", "--no-checkout", config.cortex.repo, repoPath], {
|
|
857
927
|
encoding: "utf-8",
|
|
858
928
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -873,7 +943,7 @@ function createOrphanBranch(branchName) {
|
|
|
873
943
|
} catch {
|
|
874
944
|
}
|
|
875
945
|
const repoPath = getRepoPath();
|
|
876
|
-
|
|
946
|
+
fs9.writeFileSync(path7.join(repoPath, "memories.jsonl"), "", "utf-8");
|
|
877
947
|
runGit(["add", "memories.jsonl"]);
|
|
878
948
|
runGit(["commit", "-m", `init: create cortex ${branchName}`]);
|
|
879
949
|
runGit(["push", "--set-upstream", "origin", branchName]);
|
|
@@ -890,7 +960,7 @@ function readFileFromBranch(branchName, filePath) {
|
|
|
890
960
|
}
|
|
891
961
|
function appendAndCommit(branchName, newLines, commitMessage, maxRetries = 3) {
|
|
892
962
|
const repoPath = getRepoPath();
|
|
893
|
-
const memoriesPath =
|
|
963
|
+
const memoriesPath = path7.join(repoPath, "memories.jsonl");
|
|
894
964
|
try {
|
|
895
965
|
runGit(["switch", branchName]);
|
|
896
966
|
} catch {
|
|
@@ -909,7 +979,7 @@ function appendAndCommit(branchName, newLines, commitMessage, maxRetries = 3) {
|
|
|
909
979
|
}
|
|
910
980
|
}
|
|
911
981
|
const content = newLines.join("\n") + "\n";
|
|
912
|
-
|
|
982
|
+
fs9.appendFileSync(memoriesPath, content, "utf-8");
|
|
913
983
|
runGit(["add", "memories.jsonl"]);
|
|
914
984
|
runGit(["commit", "-m", commitMessage]);
|
|
915
985
|
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
@@ -1039,7 +1109,7 @@ import readline3 from "readline";
|
|
|
1039
1109
|
import chalk10 from "chalk";
|
|
1040
1110
|
|
|
1041
1111
|
// src/lib/curator.ts
|
|
1042
|
-
import
|
|
1112
|
+
import fs10 from "fs";
|
|
1043
1113
|
import { query as query2 } from "@anthropic-ai/claude-agent-sdk";
|
|
1044
1114
|
var BASE_CURATION_PROMPT = `You are a memory curator. You evaluate recent work events and decide which ones are significant enough to become shared team memory.
|
|
1045
1115
|
|
|
@@ -1090,8 +1160,8 @@ Rules:
|
|
|
1090
1160
|
- Only add an entry if there is genuinely new information`;
|
|
1091
1161
|
function readCuratorMd() {
|
|
1092
1162
|
const mdPath = getCuratorMdPath();
|
|
1093
|
-
if (
|
|
1094
|
-
return
|
|
1163
|
+
if (fs10.existsSync(mdPath)) {
|
|
1164
|
+
return fs10.readFileSync(mdPath, "utf-8").trim();
|
|
1095
1165
|
}
|
|
1096
1166
|
return null;
|
|
1097
1167
|
}
|
|
@@ -1339,7 +1409,7 @@ var monitorCommand = new Command11("monitor").description("Show what got promote
|
|
|
1339
1409
|
// src/commands/recall.ts
|
|
1340
1410
|
import { Command as Command12 } from "commander";
|
|
1341
1411
|
import chalk12 from "chalk";
|
|
1342
|
-
import
|
|
1412
|
+
import fs11 from "fs";
|
|
1343
1413
|
var recallCommand = new Command12("recall").argument("<query>", "What to recall").description("Search memories from the cortex branch + local engrams").option("--days <n>", "Days of memories to include", "14").action(async (query3, opts) => {
|
|
1344
1414
|
const config = getConfig();
|
|
1345
1415
|
const cortex = config.cortex?.active;
|
|
@@ -1356,7 +1426,7 @@ var recallCommand = new Command12("recall").argument("<query>", "What to recall"
|
|
|
1356
1426
|
const recentMemories = allMemories.filter((m) => m.ts >= cutoff);
|
|
1357
1427
|
const matchingEngrams = searchEngrams(cortex, query3);
|
|
1358
1428
|
const ltPath = getLongtermPath(cortex);
|
|
1359
|
-
const longterm =
|
|
1429
|
+
const longterm = fs11.existsSync(ltPath) ? fs11.readFileSync(ltPath, "utf-8").trim() : null;
|
|
1360
1430
|
if (recentMemories.length > 0) {
|
|
1361
1431
|
console.log(chalk12.cyan(`Team memories (last ${days} days):`));
|
|
1362
1432
|
for (const m of recentMemories) {
|
|
@@ -1425,7 +1495,7 @@ ${memories.length} memories`));
|
|
|
1425
1495
|
// src/commands/curator-cmd.ts
|
|
1426
1496
|
import { Command as Command14 } from "commander";
|
|
1427
1497
|
import { spawnSync } from "child_process";
|
|
1428
|
-
import
|
|
1498
|
+
import fs12 from "fs";
|
|
1429
1499
|
import chalk14 from "chalk";
|
|
1430
1500
|
var CURATOR_TEMPLATE = `# Curator Guidance
|
|
1431
1501
|
|
|
@@ -1444,8 +1514,8 @@ var curatorCommand = new Command14("curator").description("Manage personal curat
|
|
|
1444
1514
|
curatorCommand.addCommand(new Command14("edit").description("Edit your curator guidance in $EDITOR").action(() => {
|
|
1445
1515
|
ensureThinkDirs();
|
|
1446
1516
|
const mdPath = getCuratorMdPath();
|
|
1447
|
-
if (!
|
|
1448
|
-
|
|
1517
|
+
if (!fs12.existsSync(mdPath)) {
|
|
1518
|
+
fs12.writeFileSync(mdPath, CURATOR_TEMPLATE, "utf-8");
|
|
1449
1519
|
}
|
|
1450
1520
|
const editor = process.env.EDITOR || "vi";
|
|
1451
1521
|
const result = spawnSync(editor, [mdPath], { stdio: "inherit" });
|
|
@@ -1457,8 +1527,8 @@ curatorCommand.addCommand(new Command14("edit").description("Edit your curator g
|
|
|
1457
1527
|
}));
|
|
1458
1528
|
curatorCommand.addCommand(new Command14("show").description("Print your current curator guidance").action(() => {
|
|
1459
1529
|
const mdPath = getCuratorMdPath();
|
|
1460
|
-
if (
|
|
1461
|
-
console.log(
|
|
1530
|
+
if (fs12.existsSync(mdPath)) {
|
|
1531
|
+
console.log(fs12.readFileSync(mdPath, "utf-8"));
|
|
1462
1532
|
} else {
|
|
1463
1533
|
console.log(chalk14.dim("No curator guidance configured. Run: think curator edit"));
|
|
1464
1534
|
}
|
|
@@ -1553,8 +1623,31 @@ configCommand.addCommand(new Command17("set").argument("<key>", "Config key (e.g
|
|
|
1553
1623
|
console.log(chalk17.green("\u2713") + ` ${key} = ${JSON.stringify(parsed)}`);
|
|
1554
1624
|
}));
|
|
1555
1625
|
|
|
1626
|
+
// src/commands/update.ts
|
|
1627
|
+
import { Command as Command18 } from "commander";
|
|
1628
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
1629
|
+
import chalk18 from "chalk";
|
|
1630
|
+
var updateCommand = new Command18("update").description("Update think to the latest version").action(() => {
|
|
1631
|
+
console.log(chalk18.cyan("Checking for updates..."));
|
|
1632
|
+
try {
|
|
1633
|
+
const result = execFileSync2("npm", ["install", "-g", "open-think@latest"], {
|
|
1634
|
+
encoding: "utf-8",
|
|
1635
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
1636
|
+
});
|
|
1637
|
+
const match = result.match(/open-think@(\S+)/);
|
|
1638
|
+
const version = match ? match[1] : "latest";
|
|
1639
|
+
console.log(chalk18.green("\u2713") + ` Updated to open-think@${version}`);
|
|
1640
|
+
} catch (err) {
|
|
1641
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1642
|
+
console.error(chalk18.red("Update failed. Try manually: npm install -g open-think@latest"));
|
|
1643
|
+
if (message.includes("EACCES")) {
|
|
1644
|
+
console.error(chalk18.dim(" You may need to run with sudo or fix npm permissions."));
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
});
|
|
1648
|
+
|
|
1556
1649
|
// src/index.ts
|
|
1557
|
-
var program = new
|
|
1650
|
+
var program = new Command19();
|
|
1558
1651
|
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");
|
|
1559
1652
|
program.addCommand(logCommand);
|
|
1560
1653
|
program.addCommand(syncCommand);
|
|
@@ -1575,4 +1668,5 @@ program.addCommand(pullCommand);
|
|
|
1575
1668
|
program.addCommand(pauseCommand);
|
|
1576
1669
|
program.addCommand(resumeCommand);
|
|
1577
1670
|
program.addCommand(configCommand);
|
|
1671
|
+
program.addCommand(updateCommand);
|
|
1578
1672
|
program.parse();
|