@binarycheater/research-sidecar 0.1.0

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 (92) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +244 -0
  3. package/README.zh.md +244 -0
  4. package/bin/research-sidecar.mjs +87 -0
  5. package/dist/client/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  6. package/dist/client/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  7. package/dist/client/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  8. package/dist/client/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  9. package/dist/client/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  10. package/dist/client/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  11. package/dist/client/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  12. package/dist/client/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  13. package/dist/client/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  14. package/dist/client/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  15. package/dist/client/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  16. package/dist/client/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  17. package/dist/client/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  18. package/dist/client/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  19. package/dist/client/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  20. package/dist/client/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  21. package/dist/client/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  22. package/dist/client/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  23. package/dist/client/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  24. package/dist/client/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  25. package/dist/client/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  26. package/dist/client/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  27. package/dist/client/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  28. package/dist/client/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  29. package/dist/client/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  30. package/dist/client/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  31. package/dist/client/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  32. package/dist/client/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  33. package/dist/client/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  34. package/dist/client/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  35. package/dist/client/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  36. package/dist/client/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  37. package/dist/client/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  38. package/dist/client/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  39. package/dist/client/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  40. package/dist/client/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  41. package/dist/client/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  42. package/dist/client/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  43. package/dist/client/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  44. package/dist/client/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  45. package/dist/client/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  46. package/dist/client/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  47. package/dist/client/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  48. package/dist/client/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  49. package/dist/client/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  50. package/dist/client/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  51. package/dist/client/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  52. package/dist/client/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  53. package/dist/client/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  54. package/dist/client/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  55. package/dist/client/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  56. package/dist/client/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  57. package/dist/client/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  58. package/dist/client/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  59. package/dist/client/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  60. package/dist/client/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  61. package/dist/client/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  62. package/dist/client/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  63. package/dist/client/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  64. package/dist/client/assets/index-BpVgCKdz.css +1 -0
  65. package/dist/client/assets/index-D7VDrQ1Q.js +324 -0
  66. package/dist/client/index.html +13 -0
  67. package/dist-server/lib/context.js +70 -0
  68. package/dist-server/lib/files.js +118 -0
  69. package/dist-server/lib/graphDiscovery.js +69 -0
  70. package/dist-server/lib/openaiProvider.js +89 -0
  71. package/dist-server/lib/prompt.js +30 -0
  72. package/dist-server/lib/researchGraph.js +144 -0
  73. package/dist-server/lib/researchGraphManifest.js +221 -0
  74. package/dist-server/lib/sidebarLayout.js +17 -0
  75. package/dist-server/lib/store.js +190 -0
  76. package/dist-server/lib/tools.js +205 -0
  77. package/dist-server/lib/types.js +1 -0
  78. package/dist-server/lib/workspaceInstall.js +157 -0
  79. package/dist-server/lib/workspaceMeta.js +171 -0
  80. package/dist-server/server/config.js +82 -0
  81. package/dist-server/server/index.js +365 -0
  82. package/package.json +83 -0
  83. package/scripts/codex-sidecar.mjs +325 -0
  84. package/scripts/prepare-package.mjs +14 -0
  85. package/skills/research-graph-sop/SKILL.md +183 -0
  86. package/skills/research-graph-sop/agents/openai.yaml +4 -0
  87. package/skills/scholar-mode/SKILL.md +34 -0
  88. package/skills/scholar-mode/agents/openai.yaml +4 -0
  89. package/skills/sidecar-thinking/SKILL.md +67 -0
  90. package/skills/sidecar-thinking/agents/openai.yaml +4 -0
  91. package/skills/writing-explanatory-reports/SKILL.md +134 -0
  92. package/skills/writing-explanatory-reports/agents/openai.yaml +4 -0
@@ -0,0 +1,325 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { access, cp, mkdir, readdir, readFile, writeFile } from "node:fs/promises";
4
+ import { dirname, join, resolve } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+
7
+ const args = parseArgs(process.argv.slice(2));
8
+ const baseUrl = args.url || "http://localhost:4317";
9
+ const scriptDir = dirname(fileURLToPath(import.meta.url));
10
+ const appRoot = resolve(scriptDir, "..");
11
+ const repoRoot = resolve(appRoot, "..");
12
+ const defaultAllowedWriteExtensions = [".md", ".markdown", ".html", ".htm", ".yaml", ".yml"];
13
+
14
+ if (args.help) {
15
+ console.log(`Usage:
16
+ npm run codex:install -- --workspace /path/to/research-repo
17
+ npm run codex:call -- --title "Review" --context "Codex summary" --file SKILL.md --question "What should the sidecar inspect?"
18
+ npm run codex:ask -- --title "Review" --context "Codex summary" --question "What is the weakest assumption?"
19
+ npm run codex:session -- --title "Review" --context "Codex summary" --file SKILL.md --file notes.md
20
+
21
+ Options:
22
+ --url Sidecar server URL. Default: http://localhost:4317
23
+ --workspace Workspace directory to initialize for install mode.
24
+ --graph Graph manifest path inside the workspace. Default: research/graph.yaml
25
+ --no-graph Do not create a starter graph manifest.
26
+ --no-skills Do not copy bundled skills into the workspace.
27
+ --force Overwrite install-managed files when possible.
28
+ --title Session title.
29
+ --context Manual context packet notes.
30
+ --file Workspace-relative file path. Repeatable.
31
+ --question Optional user question for call mode; required prompt for ask mode.
32
+ --model Model string. Default server model is used when omitted.
33
+ --api responses or chat.
34
+ `);
35
+ process.exit(0);
36
+ }
37
+
38
+ if (args.command === "install") {
39
+ await installWorkspace(args);
40
+ process.exit(0);
41
+ }
42
+
43
+ if (args.command !== "call" && args.command !== "session" && args.command !== "ask") {
44
+ throw new Error(`Unknown command: ${args.command}`);
45
+ }
46
+
47
+ const session = await request(`${baseUrl}/api/sessions`, {
48
+ method: "POST",
49
+ body: JSON.stringify({
50
+ title: args.title || "Codex handoff",
51
+ model: args.model,
52
+ apiMode: args.api
53
+ })
54
+ });
55
+
56
+ if (args.context) {
57
+ await request(`${baseUrl}/api/sessions/${session.id}`, {
58
+ method: "PATCH",
59
+ body: JSON.stringify({ manualContext: args.context })
60
+ });
61
+ }
62
+
63
+ for (const file of args.files) {
64
+ await request(`${baseUrl}/api/sessions/${session.id}/files`, {
65
+ method: "POST",
66
+ body: JSON.stringify({ path: file })
67
+ });
68
+ }
69
+
70
+ if (args.command !== "ask" && args.question) {
71
+ await request(`${baseUrl}/api/sessions/${session.id}/messages`, {
72
+ method: "POST",
73
+ body: JSON.stringify({ role: "user", content: args.question })
74
+ });
75
+ }
76
+
77
+ console.log("Sidecar session ready");
78
+ console.log(`${baseUrl}`);
79
+ console.log(`Session: ${session.id}`);
80
+ console.log(`Open: ${baseUrl}`);
81
+
82
+ if (args.command === "ask") {
83
+ if (!args.question) {
84
+ throw new Error("--question is required for ask mode.");
85
+ }
86
+ console.log("");
87
+ console.log("Sidecar answer:");
88
+ await streamAnswer(`${baseUrl}/api/sessions/${session.id}/stream`, {
89
+ message: args.question,
90
+ enableTools: true,
91
+ includeInstructionFiles: false
92
+ });
93
+ console.log("");
94
+ }
95
+
96
+ function parseArgs(raw) {
97
+ const parsed = { command: "call", files: [] };
98
+ const first = raw[0];
99
+ const start = first && !first.startsWith("-") ? 1 : 0;
100
+ if (start) {
101
+ parsed.command = first;
102
+ }
103
+ for (let i = start; i < raw.length; i += 1) {
104
+ const key = raw[i];
105
+ const value = raw[i + 1];
106
+ if (key === "--help" || key === "-h") parsed.help = true;
107
+ if (key === "--url") parsed.url = value, i += 1;
108
+ if (key === "--workspace") parsed.workspace = value, i += 1;
109
+ if (key === "--graph") parsed.graph = value, i += 1;
110
+ if (key === "--no-graph") parsed.noGraph = true;
111
+ if (key === "--no-skills") parsed.noSkills = true;
112
+ if (key === "--force") parsed.force = true;
113
+ if (key === "--title") parsed.title = value, i += 1;
114
+ if (key === "--context") parsed.context = value, i += 1;
115
+ if (key === "--file") parsed.files.push(value), i += 1;
116
+ if (key === "--question") parsed.question = value, i += 1;
117
+ if (key === "--model") parsed.model = value, i += 1;
118
+ if (key === "--api") parsed.api = value, i += 1;
119
+ }
120
+ return parsed;
121
+ }
122
+
123
+ async function installWorkspace(args) {
124
+ const workspaceRoot = resolve(args.workspace || process.cwd());
125
+ const graphManifestPath = normalizeWorkspacePath(args.graph || "research/graph.yaml");
126
+ const sideDir = join(workspaceRoot, ".side");
127
+ const sessionsDir = join(sideDir, "sessions");
128
+
129
+ await mkdir(workspaceRoot, { recursive: true });
130
+ await mkdir(sessionsDir, { recursive: true });
131
+ await writeSidecarConfig(join(sideDir, "config.json"), graphManifestPath);
132
+ await writeFileIfMissing(join(sessionsDir, "index.json"), `${JSON.stringify({ sessions: [] }, null, 2)}\n`);
133
+ await ensureGitignore(workspaceRoot);
134
+
135
+ if (!args.noSkills) {
136
+ await installBundledSkills(workspaceRoot, Boolean(args.force));
137
+ }
138
+
139
+ if (!args.noGraph) {
140
+ await installStarterGraph(workspaceRoot, graphManifestPath, Boolean(args.force));
141
+ }
142
+
143
+ console.log("Research Sidecar workspace installed");
144
+ console.log(`Workspace: ${workspaceRoot}`);
145
+ console.log(`Config: ${join(sideDir, "config.json")}`);
146
+ console.log(`Graph: ${graphManifestPath}${args.noGraph ? " (not created)" : ""}`);
147
+ console.log("");
148
+ console.log("Run from a home/user-level Sidecar install:");
149
+ console.log(`SIDECAR_WORKSPACE_ROOT="${workspaceRoot}" npm run dev`);
150
+ console.log("");
151
+ console.log("Or, if the Sidecar app itself is copied into this workspace:");
152
+ console.log("cd sidecar && npm run dev");
153
+ }
154
+
155
+ async function writeSidecarConfig(configPath, graphManifestPath) {
156
+ const existing = await readJsonFile(configPath);
157
+ const config = {
158
+ ...existing,
159
+ graph: {
160
+ ...(existing.graph || {}),
161
+ manifestPath: graphManifestPath
162
+ },
163
+ tools: {
164
+ allowedWriteExtensions: defaultAllowedWriteExtensions,
165
+ ...(existing.tools || {})
166
+ }
167
+ };
168
+ await mkdir(dirname(configPath), { recursive: true });
169
+ await writeFile(configPath, `${JSON.stringify(config, null, 2)}\n`, "utf8");
170
+ }
171
+
172
+ async function installBundledSkills(workspaceRoot, force) {
173
+ const sourceRoot = await findBundledSkillsRoot();
174
+ if (!sourceRoot) return;
175
+ const names = await readdir(sourceRoot, { withFileTypes: true });
176
+ await mkdir(join(workspaceRoot, "skills"), { recursive: true });
177
+ for (const entry of names) {
178
+ if (!entry.isDirectory()) continue;
179
+ const source = join(sourceRoot, entry.name);
180
+ const target = join(workspaceRoot, "skills", entry.name);
181
+ if (!force && (await exists(target))) continue;
182
+ await cp(source, target, { recursive: true, force, errorOnExist: false });
183
+ }
184
+ }
185
+
186
+ async function findBundledSkillsRoot() {
187
+ for (const candidate of [join(repoRoot, "skills"), join(appRoot, "skills")]) {
188
+ if (await exists(candidate)) return candidate;
189
+ }
190
+ return null;
191
+ }
192
+
193
+ async function installStarterGraph(workspaceRoot, graphManifestPath, force) {
194
+ const graphPath = join(workspaceRoot, graphManifestPath);
195
+ const graphDir = dirname(graphPath);
196
+ const notePath = join(graphDir, "rq.main.md");
197
+ const graphBody = `root: rq.main
198
+
199
+ ui:
200
+ layout: LR
201
+ expanded: [rq.main]
202
+
203
+ nodes:
204
+ - id: rq.main
205
+ title: Core research question
206
+ type: question
207
+ file: ./rq.main.md
208
+ status: active
209
+ tags: [framing]
210
+
211
+ edges: []
212
+ `;
213
+ const noteBody = `# Core research question
214
+
215
+ Use this note as the first graph node, or replace it with your existing research Markdown.
216
+ `;
217
+ await mkdir(graphDir, { recursive: true });
218
+ await writeFileMaybe(graphPath, graphBody, force);
219
+ await writeFileMaybe(notePath, noteBody, force);
220
+ }
221
+
222
+ async function ensureGitignore(workspaceRoot) {
223
+ const path = join(workspaceRoot, ".gitignore");
224
+ const current = await readTextFile(path);
225
+ if (current === null) {
226
+ await writeFile(path, ".side/\n", "utf8");
227
+ return;
228
+ }
229
+ if (!current.split(/\r?\n/).some((line) => line.trim() === ".side/" || line.trim() === ".side")) {
230
+ const suffix = current.endsWith("\n") || current.length === 0 ? "" : "\n";
231
+ await writeFile(path, `${current}${suffix}.side/\n`, "utf8");
232
+ }
233
+ }
234
+
235
+ async function writeFileIfMissing(path, body) {
236
+ if (await exists(path)) return;
237
+ await mkdir(dirname(path), { recursive: true });
238
+ await writeFile(path, body, "utf8");
239
+ }
240
+
241
+ async function writeFileMaybe(path, body, force) {
242
+ if (!force && (await exists(path))) return;
243
+ await mkdir(dirname(path), { recursive: true });
244
+ await writeFile(path, body, "utf8");
245
+ }
246
+
247
+ async function readJsonFile(path) {
248
+ const raw = await readTextFile(path);
249
+ if (!raw?.trim()) return {};
250
+ return JSON.parse(raw);
251
+ }
252
+
253
+ async function readTextFile(path) {
254
+ try {
255
+ return await readFile(path, "utf8");
256
+ } catch (error) {
257
+ if (error.code === "ENOENT") return null;
258
+ throw error;
259
+ }
260
+ }
261
+
262
+ async function exists(path) {
263
+ try {
264
+ await access(path);
265
+ return true;
266
+ } catch (error) {
267
+ if (error.code === "ENOENT") return false;
268
+ throw error;
269
+ }
270
+ }
271
+
272
+ function normalizeWorkspacePath(path) {
273
+ const normalized = path.trim().replace(/^\.\//, "");
274
+ if (!normalized || normalized.startsWith("../") || normalized.includes("/../") || normalized === "..") {
275
+ throw new Error("Workspace paths must stay inside the workspace.");
276
+ }
277
+ return normalized;
278
+ }
279
+
280
+ async function request(url, init) {
281
+ const response = await fetch(url, {
282
+ ...init,
283
+ headers: {
284
+ "Content-Type": "application/json",
285
+ ...(init.headers || {})
286
+ }
287
+ });
288
+ if (!response.ok) {
289
+ const body = await response.json().catch(() => ({}));
290
+ throw new Error(body.error || response.statusText);
291
+ }
292
+ return response.json();
293
+ }
294
+
295
+ async function streamAnswer(url, body) {
296
+ const response = await fetch(url, {
297
+ method: "POST",
298
+ headers: { "Content-Type": "application/json" },
299
+ body: JSON.stringify(body)
300
+ });
301
+ if (!response.ok || !response.body) {
302
+ const errorBody = await response.json().catch(() => ({}));
303
+ throw new Error(errorBody.error || response.statusText);
304
+ }
305
+
306
+ const decoder = new TextDecoder();
307
+ let buffer = "";
308
+ for await (const chunk of response.body) {
309
+ buffer += decoder.decode(chunk, { stream: true });
310
+ const frames = buffer.split("\n\n");
311
+ buffer = frames.pop() || "";
312
+ for (const frame of frames) {
313
+ if (!frame.startsWith("data: ")) {
314
+ continue;
315
+ }
316
+ const payload = JSON.parse(frame.slice(6));
317
+ if (payload.type === "delta") {
318
+ process.stdout.write(payload.delta);
319
+ }
320
+ if (payload.type === "error") {
321
+ throw new Error(payload.error);
322
+ }
323
+ }
324
+ }
325
+ }
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { cp, mkdir, rm } from "node:fs/promises";
4
+ import { dirname, join, resolve } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+
7
+ const appRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
8
+ const repoRoot = resolve(appRoot, "..");
9
+ const source = resolve(repoRoot, "skills");
10
+ const target = join(appRoot, "skills");
11
+
12
+ await rm(target, { recursive: true, force: true });
13
+ await mkdir(target, { recursive: true });
14
+ await cp(source, target, { recursive: true });
@@ -0,0 +1,183 @@
1
+ ---
2
+ name: research-graph-sop
3
+ description: Use when working in a research repository with graph.yaml, Markdown research notes, hypotheses, evidence, experiments, literature sources, or when Codex is asked to inspect, create, update, or maintain a lightweight research graph.
4
+ ---
5
+
6
+ # Research Graph SOP
7
+
8
+ Use the graph as a small, inspectable map of the research state. Put structure and relationships in `graph.yaml`; put reasoning, evidence details, quotes, experiment notes, and drafts in Markdown files.
9
+
10
+ Core principle: **thin graph, thick notes, active agent**. The graph should reduce cognitive load, not become a database the researcher has to maintain.
11
+
12
+ ## First Move
13
+
14
+ If `graph.yaml` exists, read it before proposing structure changes. Then read only the linked Markdown/source files needed to understand the active question, claims, evidence, methods, and tasks.
15
+
16
+ If no graph exists, inspect nearby research notes, source files, experiment notes, and task files. Build the smallest useful graph that lets a reader answer:
17
+
18
+ - What is being investigated?
19
+ - What is currently believed or hypothesized?
20
+ - Why believe, doubt, or test it?
21
+ - What should happen next?
22
+
23
+ ## Three Entry Points
24
+
25
+ ### Explore Current Research
26
+
27
+ Use this when opening an existing research repo or refreshing context.
28
+
29
+ 1. Identify the root question or active hypothesis.
30
+ 2. List active claims, rival explanations, methods, evidence, sources, and next tasks.
31
+ 3. Detect gaps: unsupported claims, missing rivals, unclear concepts, weak methods, stale tasks.
32
+ 4. Update the graph only when the update improves navigation or analysis.
33
+
34
+ ### Start From A Hypothesis
35
+
36
+ Use this when the user gives a hypothesis before a graph exists or before the project is well framed.
37
+
38
+ Create a minimal validation graph:
39
+
40
+ - a `claim` node for the hypothesis
41
+ - a `question` node it answers
42
+ - one or more rival `claim` nodes when plausible
43
+ - `concept` nodes only for necessary definitions or boundaries
44
+ - `method`, `evidence`, `source`, or `task` nodes only when they need tracking
45
+
46
+ Do not force every prediction, assumption, or falsifier into the graph. Put local details in the claim or method note unless they need separate links.
47
+
48
+ ### Maintain During Research
49
+
50
+ Use this after experiments, readings, observations, or analysis updates.
51
+
52
+ 1. Add or update the relevant Markdown note first when there is substantive content.
53
+ 2. Link evidence to the exact claim it bears on.
54
+ 3. Use `supports` only for genuine support, `contradicts` for negative evidence or rival pressure, and `depends_on` for assumptions.
55
+ 4. If evidence changes the research direction, revise the question, boundary, method, or task instead of forcing the old hypothesis to survive.
56
+
57
+ ## Minimal Graph Vocabulary
58
+
59
+ Prefer the existing Sidecar vocabulary:
60
+
61
+ | Node type | Use for |
62
+ | --- | --- |
63
+ | `question` | research questions and decomposed subquestions |
64
+ | `claim` | hypotheses, mechanism claims, rival explanations |
65
+ | `concept` | definitions, variables, scope, boundary conditions |
66
+ | `method` | experiments, operationalization, identification strategy |
67
+ | `evidence` | observations, results, patterns, counterexamples |
68
+ | `source` | papers, datasets, documents, external materials |
69
+ | `task` | next research action |
70
+ | `output` | draft conclusions, reports, paper sections |
71
+
72
+ | Edge kind | Use for |
73
+ | --- | --- |
74
+ | `decomposes` | breaks a question into subquestions |
75
+ | `answers` | a claim answers a question |
76
+ | `operationalizes` | a method makes a claim observable |
77
+ | `supports` | evidence/source supports a claim |
78
+ | `contradicts` | evidence/source/rival weakens a claim |
79
+ | `depends_on` | a claim or method relies on a premise |
80
+ | `cites` | a node uses a source |
81
+ | `leads_to` | a result creates a task or next step |
82
+
83
+ ## YAML Contract
84
+
85
+ When creating or editing `graph.yaml`, keep it valid, boring YAML. Use two-space indentation, arrays with `-`, no tabs, and quote strings only when they contain characters that could confuse YAML (`:`, `#`, `{}`, `[]`, leading `*`, or multiline text).
86
+
87
+ Top-level shape:
88
+
89
+ ```yaml
90
+ root: rq.main
91
+
92
+ ui:
93
+ layout: LR
94
+ expanded: [rq.main]
95
+
96
+ nodes:
97
+ - id: rq.main
98
+ title: Core research question
99
+ type: question
100
+ summary: One sentence is enough when no note exists yet.
101
+ status: active
102
+ tags: [framing]
103
+
104
+ edges:
105
+ - from: rq.main
106
+ to: claim.001
107
+ kind: answers
108
+ ```
109
+
110
+ Required:
111
+
112
+ - `root`: id of an existing node.
113
+ - `nodes`: array of node objects.
114
+ - each node: `id`, `title`, `type`.
115
+ - `edges`: array, empty is fine.
116
+ - each edge: `from`, `to`, `kind`; both ids must exist in `nodes`.
117
+
118
+ Optional node fields:
119
+
120
+ - `summary`: use for short, one-sentence nodes that do not need a document yet.
121
+ - `file`: one linked Markdown/HTML/text document.
122
+ - `files`: multiple linked documents. Use either strings or `{ path, title }` objects.
123
+ - `status`: `active`, `draft`, `blocked`, or `done`.
124
+ - `tags`: array of short labels.
125
+
126
+ Valid document-link patterns:
127
+
128
+ ```yaml
129
+ nodes:
130
+ - id: claim.short
131
+ title: Short claim
132
+ type: claim
133
+ summary: This node is intentionally just one sentence for now.
134
+
135
+ - id: evidence.single
136
+ title: Single note
137
+ type: evidence
138
+ file: ./evidence.md
139
+
140
+ - id: source.bundle
141
+ title: Source bundle
142
+ type: source
143
+ files:
144
+ - ./paper.md
145
+ - path: ./appendix.html
146
+ title: Appendix preview
147
+ ```
148
+
149
+ Do not force every node to link to a document. Add `file` or `files` only when the longer note exists or when creating the link improves navigation. Missing linked files are allowed while drafting, but they should be intentional and called out in the change report.
150
+
151
+ ## Agent Discipline
152
+
153
+ Act primarily as an active research assistant: propose subquestions, hypotheses, rival explanations, operationalizations, and next tasks.
154
+
155
+ Also apply two constraints:
156
+
157
+ - **Reviewer:** attack weak claims, hidden assumptions, unclear concepts, non-falsifiable hypotheses, and methods that build in their conclusion.
158
+ - **Gatekeeper:** do not present a claim as settled unless it has relevant evidence and plausible rival explanations have been considered.
159
+
160
+ New agent-created nodes should usually be `status: draft`. Use `active` when the researcher is actually pursuing the node, `blocked` when progress depends on missing material, and `done` only for completed tasks or stable source nodes.
161
+
162
+ ## Keep It Lightweight
163
+
164
+ Add a graph node only when the item needs to be navigated, connected, reused, challenged, or tracked over time.
165
+
166
+ Avoid:
167
+
168
+ - duplicating long Markdown content in `summary`
169
+ - making every sentence a node
170
+ - adding schema fields the current repo does not already use
171
+ - treating the graph as proof by itself
172
+ - hiding uncertainty behind tidy structure
173
+
174
+ Good graph changes make the current research easier to inspect in under a minute.
175
+
176
+ ## Change Report
177
+
178
+ When updating the graph, summarize:
179
+
180
+ - nodes added, changed, or removed
181
+ - important edges added or changed
182
+ - Markdown notes created or updated
183
+ - remaining methodological gaps or next tasks
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "Research Graph SOP"
3
+ short_description: "Build and maintain lightweight research hypothesis graphs"
4
+ default_prompt: "Use $research-graph-sop to inspect this research repo, build or update graph.yaml, and keep hypothesis testing traceable."
@@ -0,0 +1,34 @@
1
+ ---
2
+ name: scholar-mode
3
+ description: Use when a task is explicitly framed as research, scholarship, literature analysis, theory building, methodology critique, paper reading, academic writing, research notes, or evaluating a research intuition.
4
+ ---
5
+
6
+ # Scholar Mode
7
+
8
+ Act as a research collaborator, not a coding agent. Optimize for conceptual precision, evidence quality, argument strength, and readable synthesis.
9
+
10
+ ## Scope
11
+
12
+ Use for research thinking, papers, notes, claims, hypotheses, theory, methods, scholarly writing, literature reading, and judging research intuitions.
13
+
14
+ Do not apply when the user's primary request is implementation, debugging, tests, repository operations, app design, software architecture, or ordinary factual help. If a turn mixes research and implementation, apply this skill only to the research-analysis portion.
15
+
16
+ When this skill applies, do not propose code, architecture, tests, apps, agents, implementation plans, roadmaps, specs, or project structures unless explicitly requested. Do not turn research questions into software tasks or project-management framing. Do not scan repositories, run commands, install dependencies, start services, use git, or edit files unless directly useful for the research task or explicitly requested.
17
+
18
+ ## Judgment Protocol
19
+
20
+ Before answering, internally check: What is being judged? What evidence or material is available? What assumptions are doing the work? What would weaken or falsify the claim?
21
+
22
+ - Lead with the substantive conclusion, then give the basis and caveats.
23
+ - Distinguish facts, inferences, assumptions, value judgments, and open questions.
24
+ - Say weak evidence is weak; evaluate weak ideas instead of praising them by default.
25
+ - Do not invent citations, paper titles, authors, findings, or consensus.
26
+ - If relying on memory rather than checked sources, label it as memory or inference.
27
+
28
+ ## Research Graph Use
29
+
30
+ When a workspace includes `graph.yaml`, treat it as the structural map of the research project: nodes, relationships, and file pointers. Treat Markdown files as the content layer. If the user asks to update research structure, prefer editing the graph manifest and the affected Markdown notes together.
31
+
32
+ ## Output
33
+
34
+ Be brief, direct, and information-dense. For longer answers, start with a short TL;DR, then give the judgment, main reasons, caveats, and next move. Use tables only when comparison helps.
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "Scholar Mode"
3
+ short_description: "Research judgment, literature reasoning, theory, methods, and scholarly writing"
4
+ default_prompt: "Use $scholar-mode to analyze the research idea, evidence, assumptions, and next scholarly move."
@@ -0,0 +1,67 @@
1
+ ---
2
+ name: sidecar-thinking
3
+ description: Use when Codex should call the local Research Sidecar app, create or inspect a Sidecar session, hand off explicit context, maintain a research graph, or use the Sidecar API/CLI for a second-opinion review.
4
+ ---
5
+
6
+ # Sidecar Thinking
7
+
8
+ Use this skill to connect Codex with the local Research Sidecar app. The app is a workspace-aware web UI with chat sessions, a manifest-backed research graph, Markdown/HTML previews, and restricted workspace tools.
9
+
10
+ ## Start Or Find The App
11
+
12
+ If the server is not already running, start it from the workspace root:
13
+
14
+ ```bash
15
+ research-sidecar
16
+ ```
17
+
18
+ Default URL: `http://localhost:4317`.
19
+
20
+ The directory where `research-sidecar` is launched becomes the workspace root. The workspace config lives at `.side/config.json`.
21
+
22
+ ## Default Handoff
23
+
24
+ Prefer staging a session for the user:
25
+
26
+ ```bash
27
+ cd sidecar
28
+ npm run codex:call -- --title "Review" --context "Codex summary..." --file research/graph.yaml --question "What is the weakest assumption?"
29
+ ```
30
+
31
+ Use automatic ask mode only when the user explicitly wants Codex to relay the answer:
32
+
33
+ ```bash
34
+ cd sidecar
35
+ npm run codex:ask -- --title "Review" --context "Codex summary..." --question "What should Codex do next?"
36
+ ```
37
+
38
+ ## Research Graph
39
+
40
+ The graph is manifest-first:
41
+
42
+ - `graph.yaml` is the authority for nodes, edges, UI defaults, and file pointers.
43
+ - Markdown and HTML files hold node content.
44
+ - Markdown frontmatter can supply local metadata, but `graph.yaml` wins on conflicts.
45
+ - Graph file links are relative to the graph manifest directory by default. Use a leading `/` for an explicit workspace-root-relative link.
46
+
47
+ Default manifest path: `research/graph.yaml`. Override with `SIDECAR_GRAPH_MANIFEST` or `.side/config.json` at `graph.manifestPath`. The UI can discover graph files across the workspace and save the selected graph to `.side/config.json`.
48
+
49
+ ## API Quick Reference
50
+
51
+ - `GET /api/config`
52
+ - `PATCH /api/config`
53
+ - `GET /api/graphs`
54
+ - `GET /api/workspace`
55
+ - `POST /api/workspace/skills/install`
56
+ - `GET /api/workspace/file?path=<workspace-relative-path>`
57
+ - `GET /api/workspace/raw?path=<workspace-relative-path>`
58
+ - `GET /api/research-graph`
59
+ - `GET /api/sessions`
60
+ - `POST /api/sessions`
61
+ - `GET /api/sessions/:id`
62
+ - `PATCH /api/sessions/:id`
63
+ - `POST /api/sessions/:id/files`
64
+ - `POST /api/sessions/:id/messages`
65
+ - `POST /api/sessions/:id/stream`
66
+
67
+ All workspace paths are constrained to the configured workspace root.
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "Sidecar Thinking"
3
+ short_description: "Call the local Research Sidecar app and maintain its research graph"
4
+ default_prompt: "Use $sidecar-thinking to create a Sidecar review session, inspect the graph, or update graph-backed research notes."