open-research 0.1.0 → 0.1.1

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 +27 -1
  2. package/dist/cli.js +87 -9
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -4,11 +4,37 @@ Local-first research CLI agent. Discover papers, synthesize notes, run analysis,
4
4
 
5
5
  ## Install
6
6
 
7
+ Requires Node.js 20+.
8
+
9
+ **curl**
10
+ ```bash
11
+ curl -fsSL https://raw.githubusercontent.com/gangj277/open-research/main/install.sh | bash
12
+ ```
13
+
14
+ **npm**
7
15
  ```bash
8
16
  npm install -g open-research
9
17
  ```
10
18
 
11
- Requires Node.js 20+.
19
+ **bun**
20
+ ```bash
21
+ bun install -g open-research
22
+ ```
23
+
24
+ **pnpm**
25
+ ```bash
26
+ pnpm add -g open-research
27
+ ```
28
+
29
+ **yarn**
30
+ ```bash
31
+ yarn global add open-research
32
+ ```
33
+
34
+ **npx** (no install, runs latest)
35
+ ```bash
36
+ npx open-research
37
+ ```
12
38
 
13
39
  ## Quick Start
14
40
 
package/dist/cli.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  // src/cli.ts
4
4
  import React4 from "react";
5
- import path16 from "path";
5
+ import path17 from "path";
6
6
  import { Command } from "commander";
7
7
  import { render } from "ink";
8
8
 
@@ -848,7 +848,7 @@ async function ensureOpenResearchConfig(options) {
848
848
  }
849
849
 
850
850
  // src/tui/app.tsx
851
- import path15 from "path";
851
+ import path16 from "path";
852
852
  import {
853
853
  startTransition,
854
854
  useDeferredValue,
@@ -5033,6 +5033,81 @@ function ConfigScreen({ items, onUpdate, onClose }) {
5033
5033
  ] });
5034
5034
  }
5035
5035
 
5036
+ // src/lib/cli/update-check.ts
5037
+ import fs16 from "fs/promises";
5038
+ import path15 from "path";
5039
+ import os4 from "os";
5040
+ var PACKAGE_NAME = "open-research";
5041
+ var CHECK_INTERVAL_MS = 4 * 60 * 60 * 1e3;
5042
+ var STATE_FILE = path15.join(os4.homedir(), ".open-research", "update-check.json");
5043
+ async function readState() {
5044
+ try {
5045
+ const raw = await fs16.readFile(STATE_FILE, "utf8");
5046
+ return JSON.parse(raw);
5047
+ } catch {
5048
+ return { lastCheck: 0, latestVersion: null };
5049
+ }
5050
+ }
5051
+ async function writeState(state) {
5052
+ await fs16.mkdir(path15.dirname(STATE_FILE), { recursive: true });
5053
+ await fs16.writeFile(STATE_FILE, JSON.stringify(state), "utf8");
5054
+ }
5055
+ function getCurrentVersion() {
5056
+ try {
5057
+ const pkgPath = new URL("../../../package.json", import.meta.url);
5058
+ return process.env.npm_package_version ?? "0.0.0";
5059
+ } catch {
5060
+ return "0.0.0";
5061
+ }
5062
+ }
5063
+ async function fetchLatestVersion() {
5064
+ try {
5065
+ const controller = new AbortController();
5066
+ const timer = setTimeout(() => controller.abort(), 5e3);
5067
+ const res = await fetch(`https://registry.npmjs.org/${PACKAGE_NAME}/latest`, {
5068
+ signal: controller.signal
5069
+ });
5070
+ clearTimeout(timer);
5071
+ if (!res.ok) return null;
5072
+ const data = await res.json();
5073
+ return data.version ?? null;
5074
+ } catch {
5075
+ return null;
5076
+ }
5077
+ }
5078
+ function isNewer(latest, current) {
5079
+ const l = latest.split(".").map(Number);
5080
+ const c = current.split(".").map(Number);
5081
+ for (let i = 0; i < 3; i++) {
5082
+ if ((l[i] ?? 0) > (c[i] ?? 0)) return true;
5083
+ if ((l[i] ?? 0) < (c[i] ?? 0)) return false;
5084
+ }
5085
+ return false;
5086
+ }
5087
+ async function checkForUpdate() {
5088
+ try {
5089
+ const state = await readState();
5090
+ const now = Date.now();
5091
+ if (now - state.lastCheck < CHECK_INTERVAL_MS && state.latestVersion) {
5092
+ const current2 = getCurrentVersion();
5093
+ if (isNewer(state.latestVersion, current2)) {
5094
+ return `Update available: ${current2} \u2192 ${state.latestVersion}. Run: npm update -g open-research`;
5095
+ }
5096
+ return null;
5097
+ }
5098
+ const latest = await fetchLatestVersion();
5099
+ await writeState({ lastCheck: now, latestVersion: latest });
5100
+ if (!latest) return null;
5101
+ const current = getCurrentVersion();
5102
+ if (isNewer(latest, current)) {
5103
+ return `Update available: ${current} \u2192 ${latest}. Run: npm update -g open-research`;
5104
+ }
5105
+ return null;
5106
+ } catch {
5107
+ return null;
5108
+ }
5109
+ }
5110
+
5036
5111
  // src/tui/commands.ts
5037
5112
  var SLASH_COMMANDS = [
5038
5113
  { name: "auth", aliases: ["/connect", "/login"], description: "Connect your OpenAI account via browser OAuth", category: "auth" },
@@ -5605,6 +5680,9 @@ function App({
5605
5680
  setTheme(cfg.theme);
5606
5681
  const auth2 = await loadStoredAuth({ homeDir });
5607
5682
  setAuthStatus(auth2 ? "connected" : "missing");
5683
+ checkForUpdate().then((msg) => {
5684
+ if (msg) addSystemMessage(msg);
5685
+ });
5608
5686
  })();
5609
5687
  }, [homeDir]);
5610
5688
  useEffect2(() => {
@@ -6510,7 +6588,7 @@ function App({
6510
6588
  statusParts,
6511
6589
  statusColor,
6512
6590
  tokenDisplay,
6513
- workspaceName: hasWorkspace ? path15.basename(workspacePath) : process.cwd(),
6591
+ workspaceName: hasWorkspace ? path16.basename(workspacePath) : process.cwd(),
6514
6592
  mode: agentMode,
6515
6593
  planningStatus: planningState.status
6516
6594
  }
@@ -6522,7 +6600,7 @@ function App({
6522
6600
  var program = new Command();
6523
6601
  program.name("open-research").description("Local-first research CLI powered by ChatGPT/Codex auth.").argument("[workspacePath]", "Optional workspace path to open").action(async (workspacePath) => {
6524
6602
  await ensureOpenResearchConfig();
6525
- const target = workspacePath ? path16.resolve(workspacePath) : process.cwd();
6603
+ const target = workspacePath ? path17.resolve(workspacePath) : process.cwd();
6526
6604
  const project = await loadWorkspaceProject(target);
6527
6605
  const auth2 = await loadStoredAuth();
6528
6606
  render(
@@ -6544,7 +6622,7 @@ program.name("open-research").description("Local-first research CLI powered by C
6544
6622
  });
6545
6623
  program.command("init").argument("[workspacePath]").description("Initialize an Open Research workspace.").action(async (workspacePath) => {
6546
6624
  await ensureOpenResearchConfig();
6547
- const target = path16.resolve(workspacePath ?? process.cwd());
6625
+ const target = path17.resolve(workspacePath ?? process.cwd());
6548
6626
  const project = await initWorkspace({ workspaceDir: target });
6549
6627
  console.log(`Initialized workspace: ${target}`);
6550
6628
  console.log(`Title: ${project.title}`);
@@ -6613,8 +6691,8 @@ skills.command("create").argument("[name]").description("Scaffold a new user ski
6613
6691
  });
6614
6692
  skills.command("edit").argument("<name>").description("Open a user skill in $EDITOR.").action(async (name) => {
6615
6693
  await ensureOpenResearchConfig();
6616
- const skillDir = path16.join(getOpenResearchSkillsDir(), name);
6617
- openInEditor(path16.join(skillDir, "SKILL.md"));
6694
+ const skillDir = path17.join(getOpenResearchSkillsDir(), name);
6695
+ openInEditor(path17.join(skillDir, "SKILL.md"));
6618
6696
  const validation = await validateSkillDirectory({ skillDir });
6619
6697
  if (!validation.ok) {
6620
6698
  console.error(validation.errors.join("\n"));
@@ -6625,9 +6703,9 @@ skills.command("edit").argument("<name>").description("Open a user skill in $EDI
6625
6703
  });
6626
6704
  skills.command("validate").argument("[nameOrPath]").description("Validate one user skill.").action(async (nameOrPath) => {
6627
6705
  await ensureOpenResearchConfig();
6628
- const skillDir = nameOrPath ? path16.isAbsolute(nameOrPath) ? nameOrPath : path16.join(getOpenResearchSkillsDir(), nameOrPath) : getOpenResearchSkillsDir();
6706
+ const skillDir = nameOrPath ? path17.isAbsolute(nameOrPath) ? nameOrPath : path17.join(getOpenResearchSkillsDir(), nameOrPath) : getOpenResearchSkillsDir();
6629
6707
  const stat = await import("fs/promises").then(
6630
- (fs16) => fs16.stat(skillDir).catch(() => null)
6708
+ (fs17) => fs17.stat(skillDir).catch(() => null)
6631
6709
  );
6632
6710
  if (!stat) {
6633
6711
  throw new Error(`Skill path not found: ${skillDir}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-research",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Local-first research CLI agent — discover papers, synthesize notes, run analysis, and draft artifacts from your terminal.",
5
5
  "type": "module",
6
6
  "license": "MIT",