research-copilot 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/README.md CHANGED
@@ -41,6 +41,28 @@ Then reload your shell: `source ~/.zshrc`
41
41
 
42
42
  ---
43
43
 
44
+ ## How is Research Copilot different from Claude Cowork?
45
+
46
+ [Claude Cowork](https://www.anthropic.com/product/claude-cowork) is Anthropic's general-purpose autonomous agent for knowledge workers — it handles file organization, document drafting, and data extraction across everyday desktop tasks.
47
+
48
+ Research Copilot is a **vertical tool built specifically for academic research**. The two differ in depth, not surface:
49
+
50
+ | | Claude Cowork | Research Copilot |
51
+ |---|---|---|
52
+ | **Scope** | Horizontal — any knowledge work | Vertical — academic research lifecycle |
53
+ | **Literature** | No academic search | Multi-source search (Semantic Scholar, arXiv, OpenAlex, DBLP) with relevance scoring, coverage tracking, and citation tracing |
54
+ | **Paper management** | Processes files you already have | Structured artifact system with DOI, bibtex, citeKey, citation counts, and relevance metadata |
55
+ | **Academic writing** | Generic document drafting | Venue-specific templates (NeurIPS, ICML, journals), IMRAD structure, LaTeX, citation verification (never hallucinated) |
56
+ | **Grant writing** | None | Agency-specific guidance (NSF, NIH, DOE, DARPA, NSTC) with compliance checklists |
57
+ | **Data analysis** | Extracts data from documents | LLM-generated Python scripts with statistical modeling, matplotlib/seaborn visualization, and output manifests |
58
+ | **Domain skills** | General capabilities | 13 pluggable research skills (scientific writing, visualization, scholar evaluation, etc.) — extensible via Markdown |
59
+ | **Knowledge persistence** | Not specified | Artifact store, session summaries, cross-session memory, @-mention references |
60
+ | **Openness** | Closed-source commercial product | Open source (MIT) — fully customizable |
61
+
62
+ **In short**: Claude Cowork is like a smart office assistant. Research Copilot is like a lab partner who knows how to search literature, run stats, write papers, and apply for grants.
63
+
64
+ ---
65
+
44
66
  ## Features
45
67
 
46
68
  ### AI Chat with Coding & Writing Tools
@@ -1244,11 +1244,56 @@ function createArtifactSearchTool(projectPath) {
1244
1244
  }
1245
1245
  };
1246
1246
  }
1247
+ function createUpdateMemoryTool(projectPath) {
1248
+ return {
1249
+ name: "update-memory",
1250
+ description: 'Write to the "## Agent Memory" section of agent.md — your persistent memory across sessions. Use this to save: user preferences, project context, key decisions, important findings. The content you provide REPLACES the entire Agent Memory section (User Instructions section is preserved automatically). Keep it concise (<5000 chars total). Consolidate and remove outdated entries rather than appending.',
1251
+ parameters: {
1252
+ type: "object",
1253
+ properties: {
1254
+ memory: {
1255
+ type: "string",
1256
+ description: 'The full content for the "## Agent Memory" section. Markdown format.'
1257
+ }
1258
+ },
1259
+ required: ["memory"]
1260
+ },
1261
+ execute: async (input) => {
1262
+ const args = input;
1263
+ const memory = String(args.memory || "").trim();
1264
+ if (!memory) return toolError("MISSING_PARAMETER", "memory content is required.", {
1265
+ suggestions: ["Provide the content to save in the Agent Memory section."]
1266
+ });
1267
+ const record = findArtifactById(projectPath, AGENT_MD_ID);
1268
+ const currentContent = record?.artifact?.type === "note" ? record.artifact.content || "" : "";
1269
+ const agentMemoryMarker = "## Agent Memory";
1270
+ const markerIdx = currentContent.indexOf(agentMemoryMarker);
1271
+ const userInstructions = markerIdx >= 0 ? currentContent.slice(0, markerIdx).trimEnd() : currentContent.split("\n").filter((l) => !l.startsWith("## Agent Memory")).join("\n").trimEnd();
1272
+ const newContent = `${userInstructions}
1273
+
1274
+ ${agentMemoryMarker}
1275
+
1276
+ ${memory}
1277
+ `;
1278
+ const updated = updateArtifact(projectPath, AGENT_MD_ID, { content: newContent });
1279
+ if (!updated) {
1280
+ return toolError("UPDATE_FAILED", "Failed to update agent.md.", {
1281
+ suggestions: ["agent.md may not exist. Try opening a project folder first."]
1282
+ });
1283
+ }
1284
+ return {
1285
+ success: true,
1286
+ data: { message: "Agent memory updated.", charCount: newContent.length }
1287
+ };
1288
+ }
1289
+ };
1290
+ }
1247
1291
  function createResearchMemoryTools(params) {
1248
1292
  return [
1249
1293
  createArtifactCreateTool(params.sessionId, params.projectPath),
1250
1294
  createArtifactUpdateTool(params.projectPath),
1251
- createArtifactSearchTool(params.projectPath)
1295
+ createArtifactSearchTool(params.projectPath),
1296
+ createUpdateMemoryTool(params.projectPath)
1252
1297
  ];
1253
1298
  }
1254
1299
  const WEB_DEFAULTS = {
@@ -1696,12 +1741,11 @@ Memory model:
1696
1741
  - Session context is maintained automatically via periodic summaries.
1697
1742
  - For quick-reference info, create a note via artifact-create({ type: "note", ... }).
1698
1743
 
1699
- Long-term memory (agent.md):
1700
- - agent.md is your persistent memory across sessions. Its content is injected into your context every turn, so anything written there will be remembered.
1701
- - agent.md has two sections: "## User Instructions" (written by the user NEVER modify this section) and "## Agent Memory" (written by you update freely).
1702
- - Use artifact-update on agent.md to save important cross-session information to the "## Agent Memory" section: user preferences, ongoing project context, key decisions, important findings.
1703
- - agent.md has a ~5000 character limit. Do NOT blindly append rewrite and consolidate the "## Agent Memory" section to keep it concise and up-to-date. Remove outdated entries. Think of it as a living summary, not a log.
1704
- - Only persist genuinely valuable information that you would need in a future session. Do not save trivial or ephemeral details.
1744
+ Long-term memory:
1745
+ - Use the update-memory tool to persist information across sessions. It writes to the "## Agent Memory" section of agent.md, which is injected into your context every turn.
1746
+ - WHEN to save: user states a preference ("always do X", "I prefer Y"), a key project decision is made, you discover something important about the project that future sessions need.
1747
+ - WHEN NOT to save: routine task results, things already visible in files/git, ephemeral conversation details.
1748
+ - Keep it concise — consolidate and remove outdated entries. It's a living summary, not a log.
1705
1749
 
1706
1750
  Coding tasks:
1707
1751
  - For code implementation, follow test-first workflow: write/update test → confirm it fails → implement → confirm it passes.
@@ -5054,7 +5098,7 @@ async function enrichPaperArtifacts(options) {
5054
5098
  onProgress?.({ paperId: paper.id, status: "done" });
5055
5099
  } catch (err) {
5056
5100
  const errorMsg = err instanceof Error ? err.message : String(err);
5057
- {
5101
+ if (debug) {
5058
5102
  console.error(`[enrich] Error enriching "${paper.title?.slice(0, 60)}":`, err);
5059
5103
  }
5060
5104
  failed++;
@@ -5066,7 +5110,7 @@ async function enrichPaperArtifacts(options) {
5066
5110
  onProgress?.({ paperId: paper.id, status: "failed" });
5067
5111
  }
5068
5112
  }
5069
- {
5113
+ if (debug) {
5070
5114
  console.log(`[enrich] Done: ${enriched} enriched, ${skipped} skipped, ${failed} failed`);
5071
5115
  }
5072
5116
  return {
@@ -5813,7 +5857,7 @@ function initializeProject(path2) {
5813
5857
  }
5814
5858
  ensureAgentMd(path2);
5815
5859
  const migration = migrateLegacyArtifacts(path2);
5816
- if (migration.updatedFiles > 0) {
5860
+ if (migration.updatedFiles > 0 && process.env.RESEARCH_COPILOT_DEBUG) {
5817
5861
  console.log(`[ResearchPilot] migrated legacy artifacts: files=${migration.updatedFiles}, literature->paper=${migration.convertedLiteratureType}, data.name removed=${migration.removedDataNameField}`);
5818
5862
  }
5819
5863
  }
@@ -5840,7 +5884,7 @@ async function ensureCoordinator(state, win, model, options) {
5840
5884
  reasoningEffort: state.currentReasoningEffort,
5841
5885
  projectPath: state.projectPath,
5842
5886
  sessionId: state.sessionId,
5843
- debug: true,
5887
+ debug: !!process.env.RESEARCH_COPILOT_DEBUG,
5844
5888
  onStream: (chunk) => {
5845
5889
  state.realtimeBuffer.appendChunk(chunk);
5846
5890
  safeSend(win, "agent:stream-chunk", chunk);
@@ -6098,23 +6142,17 @@ function registerIpcHandlers() {
6098
6142
  sessionId: state.sessionId,
6099
6143
  projectPath: state.projectPath,
6100
6144
  paperIds,
6101
- debug: true,
6145
+ debug: !!process.env.RESEARCH_COPILOT_DEBUG,
6102
6146
  onProgress: (event) => {
6103
6147
  safeSend(win, "enrich:progress", event);
6104
6148
  }
6105
6149
  });
6106
6150
  });
6107
6151
  handleWindow("mention:candidates", ({ state }, query, type) => {
6108
- if (!state.projectPath) {
6109
- console.warn("[mention:candidates] No projectPath set");
6110
- return [];
6111
- }
6152
+ if (!state.projectPath) return [];
6112
6153
  try {
6113
- const result = getCandidates(state.projectPath, type, query);
6114
- console.log(`[mention:candidates] query="${query}" type=${type} → ${result.length} candidates (project: ${state.projectPath})`);
6115
- return result;
6116
- } catch (err) {
6117
- console.error("[mention:candidates] Error:", err);
6154
+ return getCandidates(state.projectPath, type, query);
6155
+ } catch {
6118
6156
  return [];
6119
6157
  }
6120
6158
  });
package/bin/cli.mjs CHANGED
@@ -62,8 +62,11 @@ const nodePath = process.env.NODE_PATH
62
62
  : pkgNodeModules
63
63
 
64
64
  // Launch Electron with the app directory
65
+ // Suppress Electron's debug output (GPU warnings, CSP notices, etc.) in production.
66
+ // Set ELECTRON_ENABLE_LOGGING=1 to see them.
67
+ const quiet = !process.env.ELECTRON_ENABLE_LOGGING
65
68
  const child = spawn(electronPath, [appDir], {
66
- stdio: ['inherit', 'inherit', 'inherit'],
69
+ stdio: quiet ? ['ignore', 'ignore', 'ignore'] : 'inherit',
67
70
  env: { ...process.env, NODE_PATH: nodePath },
68
71
  })
69
72
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "research-copilot",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "AI-powered research assistant for scientists — literature search, data analysis, academic writing, and project management",
5
5
  "type": "module",
6
6
  "bin": {