open-research 0.1.8 → 0.1.9

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 (2) hide show
  1. package/dist/cli.js +263 -36
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -8,7 +8,7 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
8
8
 
9
9
  // src/cli.ts
10
10
  import React4 from "react";
11
- import path19 from "path";
11
+ import path21 from "path";
12
12
  import { Command } from "commander";
13
13
  import { render } from "ink";
14
14
 
@@ -811,7 +811,7 @@ function formatDateTime(value) {
811
811
  }
812
812
 
813
813
  // src/lib/cli/version.ts
814
- var PACKAGE_VERSION = "0.1.8";
814
+ var PACKAGE_VERSION = "0.1.9";
815
815
  function getPackageVersion() {
816
816
  return PACKAGE_VERSION;
817
817
  }
@@ -860,7 +860,7 @@ async function ensureOpenResearchConfig(options) {
860
860
  }
861
861
 
862
862
  // src/tui/app.tsx
863
- import path18 from "path";
863
+ import path20 from "path";
864
864
  import {
865
865
  startTransition,
866
866
  useDeferredValue,
@@ -4929,6 +4929,72 @@ async function extractAndStoreMemories(input2) {
4929
4929
  return stored;
4930
4930
  }
4931
4931
 
4932
+ // src/lib/workspace/agents-md.ts
4933
+ import fs15 from "fs/promises";
4934
+ import path14 from "path";
4935
+ var AGENTS_MD_FILENAME = "AGENTS.md";
4936
+ async function readAgentsMd(workspaceDir) {
4937
+ try {
4938
+ return await fs15.readFile(path14.join(workspaceDir, AGENTS_MD_FILENAME), "utf8");
4939
+ } catch {
4940
+ return "";
4941
+ }
4942
+ }
4943
+ async function writeAgentsMd(workspaceDir, content) {
4944
+ await fs15.writeFile(path14.join(workspaceDir, AGENTS_MD_FILENAME), content, "utf8");
4945
+ }
4946
+ var UPDATE_SYSTEM_PROMPT = `You maintain an AGENTS.md file for a research workspace. This file gives future agent sessions instant context about the project \u2014 what it's about, what's been done, key files, and current direction.
4947
+
4948
+ You will receive:
4949
+ 1. The current AGENTS.md content (may be empty on first run)
4950
+ 2. A summary of what just happened in the latest conversation turn
4951
+
4952
+ Your job: decide if AGENTS.md should be updated based on the new information. If yes, output the FULL updated AGENTS.md content. If nothing meaningful changed, output exactly "NO_UPDATE".
4953
+
4954
+ Rules:
4955
+ - Keep it concise \u2014 under 2000 characters. This gets injected into every system prompt.
4956
+ - Structure: Project overview \u2192 Key files \u2192 Current state \u2192 Research direction
4957
+ - Only include information that helps a NEW agent session pick up where this one left off
4958
+ - Don't include conversation-specific details \u2014 only durable project knowledge
4959
+ - Update incrementally \u2014 preserve existing content, add/modify what changed
4960
+ - Use markdown with ## headings`;
4961
+ var UPDATE_USER_TEMPLATE = `Current AGENTS.md:
4962
+ ---
4963
+ {CURRENT_CONTENT}
4964
+ ---
4965
+
4966
+ Latest turn summary:
4967
+ User asked: {USER_MESSAGE}
4968
+ Agent did: {AGENT_SUMMARY}
4969
+
4970
+ Should AGENTS.md be updated? If yes, output the full updated content. If no meaningful project-level changes, output exactly "NO_UPDATE".`;
4971
+ async function maybeUpdateAgentsMd(input2) {
4972
+ if (input2.userMessage.length < 15) return false;
4973
+ if (input2.userMessage.startsWith("/")) return false;
4974
+ const currentContent = await readAgentsMd(input2.workspaceDir);
4975
+ const userPrompt = UPDATE_USER_TEMPLATE.replace("{CURRENT_CONTENT}", currentContent || "(empty \u2014 first time)").replace("{USER_MESSAGE}", input2.userMessage.slice(0, 500)).replace("{AGENT_SUMMARY}", input2.agentResponse.slice(0, 1500));
4976
+ try {
4977
+ const response = await input2.provider.callLLM({
4978
+ messages: [
4979
+ { role: "system", content: UPDATE_SYSTEM_PROMPT },
4980
+ { role: "user", content: userPrompt }
4981
+ ],
4982
+ model: input2.model ?? "gpt-5.4-mini",
4983
+ maxTokens: 2048,
4984
+ temperature: 0
4985
+ });
4986
+ const result = response.content.trim();
4987
+ if (result === "NO_UPDATE" || result.length < 20) {
4988
+ return false;
4989
+ }
4990
+ const cleaned = result.replace(/^```(?:markdown)?\n?/, "").replace(/\n?```$/, "").trim();
4991
+ await writeAgentsMd(input2.workspaceDir, cleaned);
4992
+ return true;
4993
+ } catch {
4994
+ return false;
4995
+ }
4996
+ }
4997
+
4932
4998
  // src/lib/agent/runtime.ts
4933
4999
  var TOOL_DESCRIPTIONS = {
4934
5000
  read_file: (a) => `Reading ${a.file_path ?? "file"}`,
@@ -5019,7 +5085,14 @@ async function runAgentTurn(input2) {
5019
5085
  const usage = input2.sessionUsage ?? createSessionUsage();
5020
5086
  const memories = await loadMemories({ homeDir: input2.homeDir });
5021
5087
  const memoryBlock = formatMemoriesForPrompt(memories);
5022
- const fullSystemPrompt = memoryBlock ? systemPrompt + "\n\n" + memoryBlock : systemPrompt;
5088
+ const agentsMd = input2.workspace.workspaceDir ? await readAgentsMd(input2.workspace.workspaceDir).catch(() => "") : "";
5089
+ const contextBlocks = [
5090
+ systemPrompt,
5091
+ memoryBlock ? memoryBlock : null,
5092
+ agentsMd ? `## Project Context (from AGENTS.md)
5093
+ ${agentsMd}` : null
5094
+ ].filter(Boolean).join("\n\n");
5095
+ const fullSystemPrompt = contextBlocks;
5023
5096
  let messages = [
5024
5097
  { role: "system", content: fullSystemPrompt },
5025
5098
  ...input2.history,
@@ -5086,6 +5159,18 @@ async function runAgentTurn(input2) {
5086
5159
  }
5087
5160
  }).catch(() => {
5088
5161
  });
5162
+ if (input2.workspace.workspaceDir) {
5163
+ maybeUpdateAgentsMd({
5164
+ workspaceDir: input2.workspace.workspaceDir,
5165
+ userMessage: input2.message,
5166
+ agentResponse: fullText,
5167
+ provider: input2.provider,
5168
+ model: "gpt-5.4-mini"
5169
+ }).then((updated) => {
5170
+ if (updated) input2.onAgentsMdUpdated?.();
5171
+ }).catch(() => {
5172
+ });
5173
+ }
5089
5174
  return {
5090
5175
  text: fullText,
5091
5176
  proposedUpdates,
@@ -5177,8 +5262,8 @@ function classifyUpdateRisk(update) {
5177
5262
  }
5178
5263
 
5179
5264
  // src/lib/workspace/apply-update.ts
5180
- import fs15 from "fs/promises";
5181
- import path14 from "path";
5265
+ import fs16 from "fs/promises";
5266
+ import path15 from "path";
5182
5267
  function resolveRelativePath(update) {
5183
5268
  if (update.key.startsWith("path:")) {
5184
5269
  return update.key.slice(5);
@@ -5199,20 +5284,20 @@ function resolveRelativePath(update) {
5199
5284
  }
5200
5285
  async function applyProposedUpdate(workspaceDir, update) {
5201
5286
  const relativePath = resolveRelativePath(update);
5202
- const absolutePath = path14.join(workspaceDir, relativePath);
5203
- await fs15.mkdir(path14.dirname(absolutePath), { recursive: true });
5204
- await fs15.writeFile(absolutePath, update.content, "utf8");
5287
+ const absolutePath = path15.join(workspaceDir, relativePath);
5288
+ await fs16.mkdir(path15.dirname(absolutePath), { recursive: true });
5289
+ await fs16.writeFile(absolutePath, update.content, "utf8");
5205
5290
  return absolutePath;
5206
5291
  }
5207
5292
 
5208
5293
  // src/lib/workspace/sessions.ts
5209
- import fs16 from "fs/promises";
5210
- import path15 from "path";
5294
+ import fs17 from "fs/promises";
5295
+ import path16 from "path";
5211
5296
  async function appendSessionEvent(workspaceDir, sessionId, event) {
5212
5297
  const sessionsDir = getWorkspaceSessionsDir(workspaceDir);
5213
- await fs16.mkdir(sessionsDir, { recursive: true });
5214
- const sessionFile = path15.join(sessionsDir, `${sessionId}.jsonl`);
5215
- await fs16.appendFile(sessionFile, `${JSON.stringify(event)}
5298
+ await fs17.mkdir(sessionsDir, { recursive: true });
5299
+ const sessionFile = path16.join(sessionsDir, `${sessionId}.jsonl`);
5300
+ await fs17.appendFile(sessionFile, `${JSON.stringify(event)}
5216
5301
  `, "utf8");
5217
5302
  }
5218
5303
  function parseEvents(raw) {
@@ -5228,7 +5313,7 @@ async function listSessions(workspaceDir) {
5228
5313
  const sessionsDir = getWorkspaceSessionsDir(workspaceDir);
5229
5314
  let files;
5230
5315
  try {
5231
- files = await fs16.readdir(sessionsDir);
5316
+ files = await fs17.readdir(sessionsDir);
5232
5317
  } catch {
5233
5318
  return [];
5234
5319
  }
@@ -5236,7 +5321,7 @@ async function listSessions(workspaceDir) {
5236
5321
  for (const file of files) {
5237
5322
  if (!file.endsWith(".jsonl")) continue;
5238
5323
  const id = file.replace(/\.jsonl$/, "");
5239
- const raw = await fs16.readFile(path15.join(sessionsDir, file), "utf8");
5324
+ const raw = await fs17.readFile(path16.join(sessionsDir, file), "utf8");
5240
5325
  const events = parseEvents(raw);
5241
5326
  if (events.length === 0) continue;
5242
5327
  const chatTurns = events.filter((e) => e.type === "chat.turn");
@@ -5257,8 +5342,8 @@ async function listSessions(workspaceDir) {
5257
5342
  }
5258
5343
  async function loadSessionHistory(workspaceDir, sessionId) {
5259
5344
  const sessionsDir = getWorkspaceSessionsDir(workspaceDir);
5260
- const sessionFile = path15.join(sessionsDir, `${sessionId}.jsonl`);
5261
- const raw = await fs16.readFile(sessionFile, "utf8");
5345
+ const sessionFile = path16.join(sessionsDir, `${sessionId}.jsonl`);
5346
+ const raw = await fs17.readFile(sessionFile, "utf8");
5262
5347
  const events = parseEvents(raw);
5263
5348
  const messages = [];
5264
5349
  const llmHistory = [];
@@ -5370,15 +5455,15 @@ function ConfigScreen({ items, onUpdate, onClose }) {
5370
5455
  }
5371
5456
 
5372
5457
  // src/lib/cli/update-check.ts
5373
- import fs17 from "fs/promises";
5374
- import path16 from "path";
5458
+ import fs18 from "fs/promises";
5459
+ import path17 from "path";
5375
5460
  import os4 from "os";
5376
5461
  var PACKAGE_NAME = "open-research";
5377
5462
  var CHECK_INTERVAL_MS = 4 * 60 * 60 * 1e3;
5378
- var STATE_FILE = path16.join(os4.homedir(), ".open-research", "update-check.json");
5463
+ var STATE_FILE = path17.join(os4.homedir(), ".open-research", "update-check.json");
5379
5464
  async function readState() {
5380
5465
  try {
5381
- const raw = await fs17.readFile(STATE_FILE, "utf8");
5466
+ const raw = await fs18.readFile(STATE_FILE, "utf8");
5382
5467
  const parsed = JSON.parse(raw);
5383
5468
  return {
5384
5469
  lastCheck: typeof parsed.lastCheck === "number" ? parsed.lastCheck : 0,
@@ -5390,8 +5475,8 @@ async function readState() {
5390
5475
  }
5391
5476
  }
5392
5477
  async function writeState(state) {
5393
- await fs17.mkdir(path16.dirname(STATE_FILE), { recursive: true });
5394
- await fs17.writeFile(STATE_FILE, JSON.stringify(state), "utf8");
5478
+ await fs18.mkdir(path17.dirname(STATE_FILE), { recursive: true });
5479
+ await fs18.writeFile(STATE_FILE, JSON.stringify(state), "utf8");
5395
5480
  }
5396
5481
  function getCurrentVersion() {
5397
5482
  return getPackageVersion();
@@ -5443,10 +5528,136 @@ async function checkForUpdate() {
5443
5528
  }
5444
5529
  }
5445
5530
 
5531
+ // src/lib/workspace/init-agents-md.ts
5532
+ import fs19 from "fs/promises";
5533
+ import path18 from "path";
5534
+ var IGNORED_DIRS2 = /* @__PURE__ */ new Set([
5535
+ "node_modules",
5536
+ ".git",
5537
+ "dist",
5538
+ "build",
5539
+ ".next",
5540
+ "__pycache__",
5541
+ ".venv",
5542
+ "venv",
5543
+ ".cache",
5544
+ "target",
5545
+ ".open-research",
5546
+ "coverage"
5547
+ ]);
5548
+ var INTERESTING_FILES = /* @__PURE__ */ new Set([
5549
+ "package.json",
5550
+ "pyproject.toml",
5551
+ "Cargo.toml",
5552
+ "go.mod",
5553
+ "requirements.txt",
5554
+ "setup.py",
5555
+ "setup.cfg",
5556
+ "README.md",
5557
+ "README.txt",
5558
+ "readme.md",
5559
+ "Makefile",
5560
+ "Dockerfile",
5561
+ "docker-compose.yml",
5562
+ ".env.example",
5563
+ "tsconfig.json",
5564
+ "vitest.config.ts",
5565
+ "jest.config.js"
5566
+ ]);
5567
+ async function scanDirectoryShallow(dir, maxDepth = 2, depth = 0) {
5568
+ const results = [];
5569
+ if (depth > maxDepth) return results;
5570
+ try {
5571
+ const entries = await fs19.readdir(dir, { withFileTypes: true });
5572
+ for (const entry of entries) {
5573
+ if (IGNORED_DIRS2.has(entry.name)) continue;
5574
+ if (entry.name.startsWith(".") && depth === 0 && entry.isDirectory()) continue;
5575
+ const fullPath = path18.join(dir, entry.name);
5576
+ const relativePath = path18.relative(process.cwd(), fullPath);
5577
+ if (entry.isDirectory()) {
5578
+ results.push({ path: relativePath + "/", size: 0, isDir: true });
5579
+ const children = await scanDirectoryShallow(fullPath, maxDepth, depth + 1);
5580
+ results.push(...children);
5581
+ } else {
5582
+ const stat = await fs19.stat(fullPath).catch(() => null);
5583
+ results.push({ path: relativePath, size: stat?.size ?? 0, isDir: false });
5584
+ }
5585
+ }
5586
+ } catch {
5587
+ }
5588
+ return results;
5589
+ }
5590
+ async function readKeyFiles(dir) {
5591
+ const contents = {};
5592
+ for (const name of INTERESTING_FILES) {
5593
+ const filePath = path18.join(dir, name);
5594
+ try {
5595
+ const content = await fs19.readFile(filePath, "utf8");
5596
+ contents[name] = content.slice(0, 2e3);
5597
+ } catch {
5598
+ }
5599
+ }
5600
+ return contents;
5601
+ }
5602
+ var INIT_PROMPT = `You are creating an AGENTS.md file for a research workspace. This file will be injected into an AI research agent's system prompt every session to give it instant project context.
5603
+
5604
+ Based on the directory structure and key file contents below, write a concise AGENTS.md that covers:
5605
+
5606
+ ## Project Overview
5607
+ What is this project about? What type of research?
5608
+
5609
+ ## Structure
5610
+ Key directories and what they contain (only the important ones).
5611
+
5612
+ ## Key Files
5613
+ Important files and what they do (only the notable ones).
5614
+
5615
+ ## Research Context
5616
+ What research appears to be in progress based on the files?
5617
+
5618
+ ## Development
5619
+ How to build/run/test (if applicable, based on package.json or similar).
5620
+
5621
+ Rules:
5622
+ - Keep it under 1500 characters total
5623
+ - Be specific to THIS project, not generic
5624
+ - If it's unclear what the project does, say so and note what you can see
5625
+ - Use markdown with ## headings
5626
+ - Don't include obvious things ("node_modules contains npm packages")`;
5627
+ async function generateInitialAgentsMd(input2) {
5628
+ const dir = input2.workspaceDir;
5629
+ const files = await scanDirectoryShallow(dir);
5630
+ const tree = files.slice(0, 100).map((f) => `${f.isDir ? "d" : "f"} ${f.path}${f.size > 0 ? ` (${(f.size / 1024).toFixed(1)}KB)` : ""}`).join("\n");
5631
+ const keyFiles = await readKeyFiles(dir);
5632
+ const keyFileText = Object.entries(keyFiles).map(([name, content2]) => `### ${name}
5633
+ \`\`\`
5634
+ ${content2}
5635
+ \`\`\``).join("\n\n");
5636
+ const userMessage = `Directory: ${dir}
5637
+
5638
+ File tree:
5639
+ ${tree}
5640
+
5641
+ ${keyFileText ? `Key files:
5642
+ ${keyFileText}` : "No recognizable key files found."}`;
5643
+ const response = await input2.provider.callLLM({
5644
+ messages: [
5645
+ { role: "system", content: INIT_PROMPT },
5646
+ { role: "user", content: userMessage.slice(0, 3e4) }
5647
+ ],
5648
+ model: input2.model ?? "gpt-5.4-mini",
5649
+ maxTokens: 2048,
5650
+ temperature: 0
5651
+ });
5652
+ const content = response.content.replace(/^```(?:markdown)?\n?/, "").replace(/\n?```$/, "").trim();
5653
+ await writeAgentsMd(dir, content);
5654
+ return content;
5655
+ }
5656
+
5446
5657
  // src/lib/preview/server.ts
5447
5658
  import http2 from "http";
5448
- import fs18 from "fs";
5449
- import path17 from "path";
5659
+ import fs20 from "fs";
5660
+ import path19 from "path";
5450
5661
 
5451
5662
  // src/lib/preview/latex-to-html.ts
5452
5663
  function latexToHtml(latex) {
@@ -5713,11 +5924,11 @@ var HTML_TEMPLATE = `<!DOCTYPE html>
5713
5924
  </body>
5714
5925
  </html>`;
5715
5926
  function startPreviewServer(texPath) {
5716
- const resolved = path17.resolve(texPath);
5927
+ const resolved = path19.resolve(texPath);
5717
5928
  let currentHash = "";
5718
5929
  function getContentHash() {
5719
5930
  try {
5720
- const content = fs18.readFileSync(resolved, "utf8");
5931
+ const content = fs20.readFileSync(resolved, "utf8");
5721
5932
  return `${content.length}-${content.slice(0, 100)}-${content.slice(-100)}`;
5722
5933
  } catch {
5723
5934
  return "error";
@@ -5725,7 +5936,7 @@ function startPreviewServer(texPath) {
5725
5936
  }
5726
5937
  function renderPage() {
5727
5938
  try {
5728
- const latex = fs18.readFileSync(resolved, "utf8");
5939
+ const latex = fs20.readFileSync(resolved, "utf8");
5729
5940
  const htmlContent = latexToHtml(latex);
5730
5941
  currentHash = getContentHash();
5731
5942
  return HTML_TEMPLATE.replace("{{CONTENT}}", htmlContent);
@@ -6484,6 +6695,20 @@ function App({
6484
6695
  addSystemMessage(`Initialized workspace "${project.title}" at ${target}`);
6485
6696
  const scanned = await scanWorkspace(target);
6486
6697
  startTransition(() => setWorkspaceFiles(scanned.files));
6698
+ if (hasAuth) {
6699
+ addSystemMessage("Generating AGENTS.md...");
6700
+ try {
6701
+ const provider = await createProviderFromStoredAuth({ homeDir });
6702
+ await generateInitialAgentsMd({
6703
+ workspaceDir: target,
6704
+ provider,
6705
+ model: "gpt-5.4-mini"
6706
+ });
6707
+ addSystemMessage("AGENTS.md created \u2014 project context will be loaded on every session.");
6708
+ } catch {
6709
+ addSystemMessage("AGENTS.md generation skipped (connect auth first).");
6710
+ }
6711
+ }
6487
6712
  } catch (err) {
6488
6713
  addSystemMessage(`Init failed: ${err instanceof Error ? err.message : String(err)}`);
6489
6714
  } finally {
@@ -7001,6 +7226,7 @@ ${msg.text}
7001
7226
  const provider = await createProviderFromStoredAuth({ homeDir });
7002
7227
  const workspace = await scanWorkspace(workspacePath);
7003
7228
  const workspaceContext = {
7229
+ workspaceDir: workspacePath,
7004
7230
  runId: sessionId,
7005
7231
  workspaceFiles: Object.fromEntries(workspace.files.map((f) => [f.key, f.content])),
7006
7232
  availableKeys: workspace.files.map((f) => f.key),
@@ -7133,6 +7359,7 @@ ${msg.text}
7133
7359
  const provider = await createProviderFromStoredAuth({ homeDir });
7134
7360
  const workspace = await scanWorkspace(workspacePath);
7135
7361
  const workspaceContext = {
7362
+ workspaceDir: workspacePath,
7136
7363
  runId: sessionId,
7137
7364
  workspaceFiles: Object.fromEntries(workspace.files.map((f) => [f.key, f.content])),
7138
7365
  availableKeys: workspace.files.map((f) => f.key),
@@ -7478,7 +7705,7 @@ ${msg.text}
7478
7705
  statusParts,
7479
7706
  statusColor,
7480
7707
  tokenDisplay,
7481
- workspaceName: hasWorkspace ? path18.basename(workspacePath) : process.cwd(),
7708
+ workspaceName: hasWorkspace ? path20.basename(workspacePath) : process.cwd(),
7482
7709
  mode: agentMode,
7483
7710
  planningStatus: planningState.status
7484
7711
  }
@@ -7490,7 +7717,7 @@ ${msg.text}
7490
7717
  var program = new Command();
7491
7718
  program.name("open-research").version(getPackageVersion()).description("Local-first research CLI powered by ChatGPT/Codex auth.").argument("[workspacePath]", "Optional workspace path to open").action(async (workspacePath) => {
7492
7719
  await ensureOpenResearchConfig();
7493
- const target = workspacePath ? path19.resolve(workspacePath) : process.cwd();
7720
+ const target = workspacePath ? path21.resolve(workspacePath) : process.cwd();
7494
7721
  const project = await loadWorkspaceProject(target);
7495
7722
  const auth2 = await loadStoredAuth();
7496
7723
  render(
@@ -7506,7 +7733,7 @@ program.name("open-research").version(getPackageVersion()).description("Local-fi
7506
7733
  });
7507
7734
  program.command("init").argument("[workspacePath]").description("Initialize an Open Research workspace.").action(async (workspacePath) => {
7508
7735
  await ensureOpenResearchConfig();
7509
- const target = path19.resolve(workspacePath ?? process.cwd());
7736
+ const target = path21.resolve(workspacePath ?? process.cwd());
7510
7737
  const project = await initWorkspace({ workspaceDir: target });
7511
7738
  console.log(`Initialized workspace: ${target}`);
7512
7739
  console.log(`Title: ${project.title}`);
@@ -7575,8 +7802,8 @@ skills.command("create").argument("[name]").description("Scaffold a new user ski
7575
7802
  });
7576
7803
  skills.command("edit").argument("<name>").description("Open a user skill in $EDITOR.").action(async (name) => {
7577
7804
  await ensureOpenResearchConfig();
7578
- const skillDir = path19.join(getOpenResearchSkillsDir(), name);
7579
- openInEditor(path19.join(skillDir, "SKILL.md"));
7805
+ const skillDir = path21.join(getOpenResearchSkillsDir(), name);
7806
+ openInEditor(path21.join(skillDir, "SKILL.md"));
7580
7807
  const validation = await validateSkillDirectory({ skillDir });
7581
7808
  if (!validation.ok) {
7582
7809
  console.error(validation.errors.join("\n"));
@@ -7587,9 +7814,9 @@ skills.command("edit").argument("<name>").description("Open a user skill in $EDI
7587
7814
  });
7588
7815
  skills.command("validate").argument("[nameOrPath]").description("Validate one user skill.").action(async (nameOrPath) => {
7589
7816
  await ensureOpenResearchConfig();
7590
- const skillDir = nameOrPath ? path19.isAbsolute(nameOrPath) ? nameOrPath : path19.join(getOpenResearchSkillsDir(), nameOrPath) : getOpenResearchSkillsDir();
7817
+ const skillDir = nameOrPath ? path21.isAbsolute(nameOrPath) ? nameOrPath : path21.join(getOpenResearchSkillsDir(), nameOrPath) : getOpenResearchSkillsDir();
7591
7818
  const stat = await import("fs/promises").then(
7592
- (fs19) => fs19.stat(skillDir).catch(() => null)
7819
+ (fs21) => fs21.stat(skillDir).catch(() => null)
7593
7820
  );
7594
7821
  if (!stat) {
7595
7822
  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.8",
3
+ "version": "0.1.9",
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",