create-academic-research 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 (96) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +160 -0
  3. package/SECURITY.md +8 -0
  4. package/dist/bin/academic-research.d.ts +2 -0
  5. package/dist/bin/academic-research.js +3 -0
  6. package/dist/bin/create-academic-research.d.ts +2 -0
  7. package/dist/bin/create-academic-research.js +3 -0
  8. package/dist/src/capabilities.d.ts +54 -0
  9. package/dist/src/capabilities.js +487 -0
  10. package/dist/src/cli.d.ts +3 -0
  11. package/dist/src/cli.js +451 -0
  12. package/dist/src/files.d.ts +8 -0
  13. package/dist/src/files.js +44 -0
  14. package/dist/src/names.d.ts +3 -0
  15. package/dist/src/names.js +64 -0
  16. package/dist/src/project.d.ts +28 -0
  17. package/dist/src/project.js +229 -0
  18. package/dist/src/prompts.d.ts +18 -0
  19. package/dist/src/prompts.js +29 -0
  20. package/dist/src/runner.d.ts +12 -0
  21. package/dist/src/runner.js +22 -0
  22. package/dist/src/stack.d.ts +30 -0
  23. package/dist/src/stack.js +180 -0
  24. package/package.json +59 -0
  25. package/template/AGENTS.md +47 -0
  26. package/template/README.md +68 -0
  27. package/template/analysis_outputs/.gitkeep +0 -0
  28. package/template/artifacts/artifact-checklist.md +7 -0
  29. package/template/artifacts/cache/.gitkeep +0 -0
  30. package/template/artifacts/data/.gitkeep +0 -0
  31. package/template/artifacts/models/.gitkeep +0 -0
  32. package/template/artifacts/releases/.gitkeep +0 -0
  33. package/template/configs/agent-stack.yaml +5 -0
  34. package/template/configs/capabilities.yaml +4 -0
  35. package/template/configs/default.yaml +15 -0
  36. package/template/data/external/.gitkeep +0 -0
  37. package/template/data/interim/.gitkeep +0 -0
  38. package/template/data/processed/.gitkeep +0 -0
  39. package/template/data/raw/.gitkeep +0 -0
  40. package/template/debug_outputs/.gitkeep +0 -0
  41. package/template/docs/agent/capability-profile.md +6 -0
  42. package/template/docs/agent/mcp-setup.md +4 -0
  43. package/template/docs/agent/output-contracts.md +8 -0
  44. package/template/docs/agent/research-program.md +3 -0
  45. package/template/docs/data_dictionary/README.md +3 -0
  46. package/template/docs/ethics/data-governance.md +3 -0
  47. package/template/docs/methodology/evaluation-plan.md +3 -0
  48. package/template/docs/methodology/research-design.md +4 -0
  49. package/template/docs/methodology/threats-to-validity.md +3 -0
  50. package/template/docs/reproducibility/README.md +3 -0
  51. package/template/docs/venue/venue-strategy.md +3 -0
  52. package/template/experiments/registry.csv +1 -0
  53. package/template/experiments/templates/experiment-record.md +23 -0
  54. package/template/explore_outputs/.gitkeep +0 -0
  55. package/template/notebooks/README.md +5 -0
  56. package/template/outputs/figures/.gitkeep +0 -0
  57. package/template/outputs/models/.gitkeep +0 -0
  58. package/template/outputs/tables/.gitkeep +0 -0
  59. package/template/package.json +17 -0
  60. package/template/pyproject.toml +41 -0
  61. package/template/reports/paper/.gitkeep +0 -0
  62. package/template/reports/proposal/.gitkeep +0 -0
  63. package/template/reports/rebuttal/README.md +3 -0
  64. package/template/reports/reviews/README.md +3 -0
  65. package/template/reports/slides/.gitkeep +0 -0
  66. package/template/repro_outputs/.gitkeep +0 -0
  67. package/template/sota/gaps.md +9 -0
  68. package/template/sota/literature-matrix.csv +1 -0
  69. package/template/sota/prisma-flow.md +4 -0
  70. package/template/sota/screening-decisions.csv +1 -0
  71. package/template/sota/search-strategy.md +14 -0
  72. package/template/sota/synthesis.md +9 -0
  73. package/template/sources/assets/.gitkeep +0 -0
  74. package/template/sources/bib/citation-audit.csv +1 -0
  75. package/template/sources/bib/references.bib +1 -0
  76. package/template/sources/conversion-ledger.csv +1 -0
  77. package/template/sources/markdown/.gitkeep +0 -0
  78. package/template/sources/metadata/.gitkeep +0 -0
  79. package/template/sources/pdfs/.gitkeep +0 -0
  80. package/template/sources/source-ledger.csv +1 -0
  81. package/template/src/project_package/__init__.py +1 -0
  82. package/template/tests/test_project_structure.py +25 -0
  83. package/template/train_outputs/.gitkeep +0 -0
  84. package/template/wiki/claims/.gitkeep +0 -0
  85. package/template/wiki/concepts/.gitkeep +0 -0
  86. package/template/wiki/contradictions.md +3 -0
  87. package/template/wiki/decisions/.gitkeep +0 -0
  88. package/template/wiki/experiments/.gitkeep +0 -0
  89. package/template/wiki/index.md +9 -0
  90. package/template/wiki/log.md +1 -0
  91. package/template/wiki/methods/.gitkeep +0 -0
  92. package/template/wiki/open_questions.md +3 -0
  93. package/template/wiki/questions/.gitkeep +0 -0
  94. package/template/wiki/sources/.gitkeep +0 -0
  95. package/template/wiki/synthesis.md +3 -0
  96. package/template/wiki/templates/.gitkeep +0 -0
@@ -0,0 +1,229 @@
1
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
2
+ import { basename, dirname, join, resolve } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import YAML from "yaml";
5
+ import { DEFAULT_AGENT, initializeCapabilities, installSkills } from "./capabilities.js";
6
+ import { copyDirectory, exists, isNonEmptyDirectory, movePath, readJson, writeJson } from "./files.js";
7
+ import { packageify, slugify, titleFromSlug } from "./names.js";
8
+ import { AGENT_STACK } from "./stack.js";
9
+ const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "../..");
10
+ const templateRoot = join(packageRoot, "template");
11
+ const REQUIRED_CSV_COLUMNS = {
12
+ "sources/source-ledger.csv": [
13
+ "source_id",
14
+ "type",
15
+ "title",
16
+ "authors",
17
+ "year",
18
+ "venue",
19
+ "identifiers",
20
+ "raw_path",
21
+ "derived_path",
22
+ "bib_path",
23
+ "status",
24
+ "relevance",
25
+ "evidence_level",
26
+ "quality_notes",
27
+ "added_on",
28
+ "last_checked"
29
+ ],
30
+ "sources/conversion-ledger.csv": [
31
+ "source_id",
32
+ "input_path",
33
+ "output_path",
34
+ "tool",
35
+ "command_or_config",
36
+ "conversion_date",
37
+ "quality",
38
+ "checked_pages",
39
+ "known_issues"
40
+ ],
41
+ "sources/bib/citation-audit.csv": [
42
+ "citation_key",
43
+ "status",
44
+ "issue",
45
+ "source_id",
46
+ "claim_or_location",
47
+ "expected_fix",
48
+ "checked_on"
49
+ ],
50
+ "sota/literature-matrix.csv": [
51
+ "source_id",
52
+ "title",
53
+ "authors",
54
+ "year",
55
+ "venue",
56
+ "method",
57
+ "dataset_or_sample",
58
+ "task_or_problem",
59
+ "key_claim",
60
+ "metric_or_result",
61
+ "limitations",
62
+ "relevance",
63
+ "citation_count_or_signal",
64
+ "identifiers"
65
+ ],
66
+ "sota/screening-decisions.csv": ["stage", "source_id", "title", "decision", "reason", "screened_by", "date"],
67
+ "experiments/registry.csv": [
68
+ "run_id",
69
+ "date",
70
+ "git_commit",
71
+ "question",
72
+ "hypothesis",
73
+ "command",
74
+ "config",
75
+ "seed",
76
+ "dataset",
77
+ "metric",
78
+ "result",
79
+ "runtime",
80
+ "status",
81
+ "record_path"
82
+ ]
83
+ };
84
+ export async function createProject(options) {
85
+ const target = resolve(options.target);
86
+ if (await isNonEmptyDirectory(target)) {
87
+ throw new Error(`target directory is not empty: ${target}`);
88
+ }
89
+ const title = options.title ?? titleFromSlug(options.slug ?? basename(target));
90
+ const slug = slugify(options.slug ?? title);
91
+ const packageName = packageify(options.packageName ?? slug);
92
+ const preset = options.preset ?? "default";
93
+ const agent = options.agent ?? DEFAULT_AGENT;
94
+ if (!AGENT_STACK.presets[preset]) {
95
+ throw new Error(`unknown capability preset: ${preset}`);
96
+ }
97
+ await mkdir(dirname(target), { recursive: true });
98
+ await copyDirectory(templateRoot, target);
99
+ await personalizeProject(target, { title, slug, packageName, profile: options.profile ?? "academic-general" });
100
+ await writeGeneratedPackageJson(target, { slug });
101
+ await writeAgentStack(target);
102
+ await initializeCapabilities(target, { preset, agent });
103
+ if (options.installSkills) {
104
+ await installSkills(target, preset);
105
+ }
106
+ return { root: target, title, slug, packageName };
107
+ }
108
+ export async function renameProject(root, options) {
109
+ const target = resolve(root);
110
+ const configPath = join(target, "configs/default.yaml");
111
+ const config = YAML.parse(await readFile(configPath, "utf8"));
112
+ const previousPackage = config.project.package;
113
+ const title = options.title ?? config.project.title;
114
+ const slug = options.slug === undefined ? config.project.slug : slugify(options.slug);
115
+ const packageName = options.packageName === undefined ? config.project.package : packageify(options.packageName);
116
+ await personalizeProject(target, {
117
+ title,
118
+ slug,
119
+ packageName,
120
+ profile: config.project.profile,
121
+ previousPackage
122
+ });
123
+ await writeGeneratedPackageJson(target, { slug });
124
+ return { root: target, title, slug, packageName };
125
+ }
126
+ export async function doctorProject(root) {
127
+ const target = resolve(root);
128
+ const errors = [];
129
+ const required = [
130
+ "README.md",
131
+ "package.json",
132
+ "pyproject.toml",
133
+ "AGENTS.md",
134
+ "configs/default.yaml",
135
+ "configs/agent-stack.yaml",
136
+ "configs/capabilities.yaml",
137
+ "docs/agent/capability-profile.md",
138
+ "docs/agent/generated",
139
+ "sources/source-ledger.csv",
140
+ "sota/literature-matrix.csv",
141
+ "wiki/index.md",
142
+ "wiki/log.md"
143
+ ];
144
+ for (const relative of required) {
145
+ if (!(await exists(join(target, relative))))
146
+ errors.push(`missing ${relative}`);
147
+ }
148
+ if (await exists(join(target, "configs/default.yaml"))) {
149
+ try {
150
+ const config = YAML.parse(await readFile(join(target, "configs/default.yaml"), "utf8"));
151
+ if (!config?.project?.package) {
152
+ errors.push("configs/default.yaml missing project.package");
153
+ }
154
+ else if (!(await exists(join(target, "src", config.project.package, "__init__.py")))) {
155
+ errors.push(`missing src/${config.project.package}/__init__.py`);
156
+ }
157
+ }
158
+ catch (error) {
159
+ errors.push(`invalid configs/default.yaml: ${error instanceof Error ? error.message : String(error)}`);
160
+ }
161
+ }
162
+ if (await exists(join(target, "configs/capabilities.yaml"))) {
163
+ try {
164
+ YAML.parse(await readFile(join(target, "configs/capabilities.yaml"), "utf8"));
165
+ }
166
+ catch (error) {
167
+ errors.push(`invalid configs/capabilities.yaml: ${error instanceof Error ? error.message : String(error)}`);
168
+ }
169
+ }
170
+ for (const [relative, requiredColumns] of Object.entries(REQUIRED_CSV_COLUMNS)) {
171
+ await validateCsvHeader(target, relative, requiredColumns, errors);
172
+ }
173
+ return { ok: errors.length === 0, errors };
174
+ }
175
+ async function personalizeProject(root, { title, slug, packageName, profile, previousPackage = "project_package" }) {
176
+ const configPath = join(root, "configs/default.yaml");
177
+ const config = YAML.parse(await readFile(configPath, "utf8"));
178
+ config.project = {
179
+ ...config.project,
180
+ slug,
181
+ title,
182
+ profile,
183
+ package: packageName
184
+ };
185
+ await writeFile(configPath, YAML.stringify(config), "utf8");
186
+ const pyprojectPath = join(root, "pyproject.toml");
187
+ const pyproject = await readFile(pyprojectPath, "utf8");
188
+ await writeFile(pyprojectPath, pyproject.replace(/^name = ".*"$/m, `name = "${slug}"`), "utf8");
189
+ const readmePath = join(root, "README.md");
190
+ const readme = await readFile(readmePath, "utf8");
191
+ await writeFile(readmePath, readme.replace(/^# .*/m, `# ${title}`), "utf8");
192
+ const previous = join(root, "src", previousPackage);
193
+ const next = join(root, "src", packageName);
194
+ if (previous !== next && (await exists(previous))) {
195
+ await movePath(previous, next);
196
+ }
197
+ else {
198
+ await mkdir(dirname(join(next, "__init__.py")), { recursive: true });
199
+ if (!(await exists(join(next, "__init__.py")))) {
200
+ await writeFile(join(next, "__init__.py"), "\"\"\"Project package.\"\"\"\n", "utf8");
201
+ }
202
+ }
203
+ }
204
+ async function writeGeneratedPackageJson(root, { slug }) {
205
+ const path = join(root, "package.json");
206
+ const data = await readJson(path);
207
+ const existingSpec = data.devDependencies?.["create-academic-research"];
208
+ const packageSpec = process.env.CREATE_ACADEMIC_RESEARCH_PACKAGE_SPEC ?? existingSpec ?? "^0.1.0";
209
+ data.name = slug;
210
+ data.devDependencies = {
211
+ ...(data.devDependencies ?? {}),
212
+ "create-academic-research": packageSpec
213
+ };
214
+ await writeJson(path, data);
215
+ }
216
+ async function writeAgentStack(root) {
217
+ await writeFile(join(root, "configs/agent-stack.yaml"), YAML.stringify(AGENT_STACK), "utf8");
218
+ }
219
+ async function validateCsvHeader(root, relative, requiredColumns, errors) {
220
+ const path = join(root, relative);
221
+ if (!(await exists(path)))
222
+ return;
223
+ const header = (await readFile(path, "utf8")).split(/\r?\n/, 1)[0] ?? "";
224
+ const columns = new Set(header.split(",").map((column) => column.trim()).filter(Boolean));
225
+ for (const column of requiredColumns) {
226
+ if (!columns.has(column))
227
+ errors.push(`${relative} missing column ${column}`);
228
+ }
229
+ }
@@ -0,0 +1,18 @@
1
+ export interface CreatePromptDefaults {
2
+ title: string;
3
+ slug: string;
4
+ packageName: string;
5
+ preset: string;
6
+ agent: string;
7
+ installSkills: boolean;
8
+ installMcpTools: boolean;
9
+ }
10
+ export interface CreatePromptAnswers extends CreatePromptDefaults {
11
+ installSkills: boolean;
12
+ installMcpTools: boolean;
13
+ }
14
+ export interface CreatePromptLocks {
15
+ installSkills?: boolean;
16
+ installMcpTools?: boolean;
17
+ }
18
+ export declare function askCreateOptions(defaults: CreatePromptDefaults, locks?: CreatePromptLocks): Promise<CreatePromptAnswers>;
@@ -0,0 +1,29 @@
1
+ import { createInterface } from "node:readline/promises";
2
+ import { stdin as input, stdout as output } from "node:process";
3
+ export async function askCreateOptions(defaults, locks = {}) {
4
+ const rl = createInterface({ input, output });
5
+ try {
6
+ const title = await question(rl, "Project title", defaults.title);
7
+ const slug = await question(rl, "Project slug", defaults.slug);
8
+ const packageName = await question(rl, "Python package", defaults.packageName);
9
+ const preset = await question(rl, "Capability preset", defaults.preset);
10
+ const agent = await question(rl, "Agent target", defaults.agent);
11
+ const installSkills = locks.installSkills ?? (await yesNo(rl, "Install project-local skills now", defaults.installSkills));
12
+ const installMcpTools = locks.installMcpTools ?? (await yesNo(rl, "Run external MCP installers now", defaults.installMcpTools));
13
+ return { title, slug, packageName, preset, agent, installSkills, installMcpTools };
14
+ }
15
+ finally {
16
+ rl.close();
17
+ }
18
+ }
19
+ async function question(rl, prompt, fallback) {
20
+ const answer = await rl.question(`${prompt} [${fallback}]: `);
21
+ return answer.trim() || fallback;
22
+ }
23
+ async function yesNo(rl, prompt, fallback) {
24
+ const label = fallback ? "Y/n" : "y/N";
25
+ const answer = (await rl.question(`${prompt} [${label}]: `)).trim().toLowerCase();
26
+ if (!answer)
27
+ return fallback;
28
+ return ["y", "yes", "s", "si"].includes(answer);
29
+ }
@@ -0,0 +1,12 @@
1
+ import { type StdioOptions } from "node:child_process";
2
+ export interface RunnerOptions {
3
+ cwd?: string;
4
+ stdio?: StdioOptions;
5
+ }
6
+ export interface CommandResult {
7
+ code: number;
8
+ }
9
+ export interface Runner {
10
+ run(command: string[], options?: RunnerOptions): Promise<CommandResult>;
11
+ }
12
+ export declare const defaultRunner: Runner;
@@ -0,0 +1,22 @@
1
+ import { spawn } from "node:child_process";
2
+ export const defaultRunner = {
3
+ run(command, options = {}) {
4
+ const executable = command[0];
5
+ if (!executable)
6
+ throw new Error("cannot run an empty command");
7
+ return new Promise((resolve, reject) => {
8
+ const child = spawn(executable, command.slice(1), {
9
+ cwd: options.cwd,
10
+ stdio: options.stdio ?? "inherit",
11
+ shell: false
12
+ });
13
+ child.on("error", reject);
14
+ child.on("exit", (code) => {
15
+ if (code === 0)
16
+ resolve({ code });
17
+ else
18
+ reject(new Error(`command failed (${code}): ${command.join(" ")}`));
19
+ });
20
+ });
21
+ }
22
+ };
@@ -0,0 +1,30 @@
1
+ export interface SkillBundle {
2
+ description: string;
3
+ commands: string[];
4
+ }
5
+ export interface CapabilityPreset {
6
+ description: string;
7
+ skill_bundles: string[];
8
+ mcp_servers: string[];
9
+ }
10
+ export interface McpServer {
11
+ priority: string;
12
+ source_need: string;
13
+ install_command: string;
14
+ uninstall_command: string;
15
+ command: string;
16
+ args: string[];
17
+ env: Record<string, string>;
18
+ smoke_test: string;
19
+ risks: string;
20
+ }
21
+ export interface AgentStack {
22
+ version: number;
23
+ description: string;
24
+ skill_bundles: Record<string, SkillBundle>;
25
+ presets: Record<string, CapabilityPreset>;
26
+ mcp_servers: Record<string, McpServer>;
27
+ }
28
+ export type McpToolCommandKey = "install_command" | "uninstall_command";
29
+ export declare const AGENT_STACK: AgentStack;
30
+ export declare function presetMcpServers(preset: string): string[];
@@ -0,0 +1,180 @@
1
+ export const AGENT_STACK = {
2
+ version: 1,
3
+ description: "Project-local academic research capability stack.",
4
+ skill_bundles: {
5
+ academic_research: {
6
+ description: "Research-native workflow skills.",
7
+ commands: [
8
+ "npm exec --yes --package skills -- skills add VincenzoImp/academic-research-skills {agent_flag} --skill '*' --copy -y"
9
+ ]
10
+ },
11
+ default_complementary: {
12
+ description: "General agent engineering, document, frontend, testing, and skill helpers.",
13
+ commands: [
14
+ "npm exec --yes --package skills -- skills add obra/superpowers {agent_flag} --skill '*' --copy -y",
15
+ "npm exec --yes --package skills -- skills add anthropics/skills {agent_flag} --skill frontend-design webapp-testing skill-creator mcp-builder pdf docx xlsx pptx --copy -y"
16
+ ]
17
+ },
18
+ docling: {
19
+ description: "PDF and document conversion helper.",
20
+ commands: [
21
+ "npm exec --yes --package skills -- skills add existential-birds/beagle {agent_flag} --skill docling --copy -y"
22
+ ]
23
+ },
24
+ optional_connectors: {
25
+ description: "CLI-side literature connectors to use only when MCP servers are unavailable.",
26
+ commands: [
27
+ "npm exec --yes --package skills -- skills add davila7/claude-code-templates {agent_flag} --skill openalex-database --copy -y",
28
+ "npm exec --yes --package skills -- skills add agents365-ai/365-skills {agent_flag} --skill semanticscholar-skill --copy -y",
29
+ "npm exec --yes --package skills -- skills add fuzhiyu/researchprojecttemplate {agent_flag} --skill zotero-paper-reader --copy -y"
30
+ ]
31
+ },
32
+ optional_mechanical_specialists: {
33
+ description: "Narrow mechanical helpers that do not replace project-native governance.",
34
+ commands: [
35
+ "npm exec --yes --package skills -- skills add bahayonghang/academic-writing-skills {agent_flag} --skill latex-paper-en --copy -y",
36
+ [
37
+ "npm exec --yes --package skills -- skills add lllllllama/ai-paper-reproduction-skill",
38
+ "{agent_flag}",
39
+ "--skill",
40
+ [
41
+ "ai-research-reproduction",
42
+ "repo-intake-and-plan",
43
+ "env-and-assets-bootstrap",
44
+ "minimal-run-and-audit",
45
+ "paper-context-resolver",
46
+ "analyze-project",
47
+ "safe-debug",
48
+ "run-train",
49
+ "explore-run",
50
+ "explore-code"
51
+ ].join(" "),
52
+ "--copy -y"
53
+ ].join(" ")
54
+ ]
55
+ }
56
+ },
57
+ presets: {
58
+ minimal: {
59
+ description: "Only the academic research skill package.",
60
+ skill_bundles: ["academic_research"],
61
+ mcp_servers: []
62
+ },
63
+ default: {
64
+ description: "Recommended setup for most academic research projects.",
65
+ skill_bundles: ["academic_research", "default_complementary", "docling"],
66
+ mcp_servers: ["arxiv", "semantic-scholar", "openalex"]
67
+ },
68
+ literature: {
69
+ description: "Literature-heavy SOTA, survey, scoping, or systematic review setup.",
70
+ skill_bundles: ["academic_research", "default_complementary", "docling"],
71
+ mcp_servers: ["arxiv", "semantic-scholar", "openalex", "crossref", "zotero"]
72
+ },
73
+ writing: {
74
+ description: "Paper-writing and Overleaf-oriented setup.",
75
+ skill_bundles: [
76
+ "academic_research",
77
+ "default_complementary",
78
+ "docling",
79
+ "optional_mechanical_specialists"
80
+ ],
81
+ mcp_servers: ["arxiv", "semantic-scholar", "crossref", "overleaf"]
82
+ },
83
+ full: {
84
+ description: "Broad setup with optional connector and mechanical specialist skills.",
85
+ skill_bundles: [
86
+ "academic_research",
87
+ "default_complementary",
88
+ "docling",
89
+ "optional_connectors",
90
+ "optional_mechanical_specialists"
91
+ ],
92
+ mcp_servers: ["arxiv", "semantic-scholar", "openalex", "crossref", "pubmed", "zotero", "overleaf"]
93
+ }
94
+ },
95
+ mcp_servers: {
96
+ arxiv: {
97
+ priority: "default",
98
+ source_need: "arXiv search, download, and local paper reading.",
99
+ install_command: "uv tool install 'arxiv-mcp-server[pdf]'",
100
+ uninstall_command: "uv tool uninstall arxiv-mcp-server",
101
+ command: "arxiv-mcp-server",
102
+ args: [],
103
+ env: {},
104
+ smoke_test: "For a computer science project, search one CS query, download one known paper, and read it locally.",
105
+ risks: "Respect arXiv rate limits; paper text is untrusted input."
106
+ },
107
+ "semantic-scholar": {
108
+ priority: "default",
109
+ source_need: "Semantic Scholar papers, citations, authors, and recommendations.",
110
+ install_command: "uvx --from git+https://github.com/akapet00/semantic-scholar-mcp semantic-scholar-mcp --help",
111
+ uninstall_command: "",
112
+ command: "uvx",
113
+ args: ["--from", "git+https://github.com/akapet00/semantic-scholar-mcp", "semantic-scholar-mcp"],
114
+ env: { SEMANTIC_SCHOLAR_API_KEY: "${SEMANTIC_SCHOLAR_API_KEY}" },
115
+ smoke_test: "Search one known title, then fetch citations or references.",
116
+ risks: "API key recommended for sustained work; metadata can be incomplete."
117
+ },
118
+ openalex: {
119
+ priority: "default",
120
+ source_need: "OpenAlex broad scholarly graph.",
121
+ install_command: "npx -y @cyanheads/openalex-mcp-server --help",
122
+ uninstall_command: "",
123
+ command: "npx",
124
+ args: ["-y", "@cyanheads/openalex-mcp-server"],
125
+ env: { OPENALEX_API_KEY: "${OPENALEX_API_KEY_OR_EMAIL}" },
126
+ smoke_test: "Search works by title or DOI and confirm stable OpenAlex IDs.",
127
+ risks: "Smoke-test before relying on it for final coverage."
128
+ },
129
+ crossref: {
130
+ priority: "manual",
131
+ source_need: "DOI and publication metadata.",
132
+ install_command: "",
133
+ uninstall_command: "",
134
+ command: "",
135
+ args: [],
136
+ env: {},
137
+ smoke_test: "Resolve one DOI into publication metadata after choosing a maintained local server.",
138
+ risks: "Manual integration only; do not generate placeholder paths."
139
+ },
140
+ pubmed: {
141
+ priority: "domain-specific",
142
+ source_need: "PubMed and biomedical literature.",
143
+ install_command: "npx -y @cyanheads/pubmed-mcp-server --help",
144
+ uninstall_command: "",
145
+ command: "npx",
146
+ args: ["-y", "@cyanheads/pubmed-mcp-server"],
147
+ env: { NCBI_API_KEY: "${NCBI_API_KEY}" },
148
+ smoke_test: "Search one PMID or title and fetch metadata.",
149
+ risks: "Domain-specific; observe NCBI rate limits."
150
+ },
151
+ zotero: {
152
+ priority: "local-library",
153
+ source_need: "Zotero local library and attachments.",
154
+ install_command: "uvx zoty --help",
155
+ uninstall_command: "",
156
+ command: "uvx",
157
+ args: ["zoty", "mcp"],
158
+ env: {},
159
+ smoke_test: "List collections, search one known item, and export BibTeX.",
160
+ risks: "Requires Zotero local API and bridge setup."
161
+ },
162
+ overleaf: {
163
+ priority: "writing",
164
+ source_need: "Overleaf project sync.",
165
+ install_command: "uv tool install overleaf-mcp-server",
166
+ uninstall_command: "uv tool uninstall overleaf-mcp-server",
167
+ command: "overleaf-mcp",
168
+ args: ["serve"],
169
+ env: { OVERLEAF_TOKEN: "${OVERLEAF_TOKEN}" },
170
+ smoke_test: "List projects or read one .tex file; do not write by default.",
171
+ risks: "Requires token setup; write access needs explicit approval."
172
+ }
173
+ }
174
+ };
175
+ export function presetMcpServers(preset) {
176
+ const config = AGENT_STACK.presets[preset];
177
+ if (!config)
178
+ throw new Error(`unknown capability preset: ${preset}`);
179
+ return [...config.mcp_servers];
180
+ }
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "create-academic-research",
3
+ "version": "0.1.0",
4
+ "description": "Create and manage agent-ready academic research repositories.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "Vincenzo Imperati",
8
+ "keywords": [
9
+ "academic-research",
10
+ "research-project",
11
+ "agent-skills",
12
+ "mcp",
13
+ "literature-review",
14
+ "reproducibility"
15
+ ],
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/VincenzoImp/create-academic-research.git"
19
+ },
20
+ "bugs": {
21
+ "url": "https://github.com/VincenzoImp/create-academic-research/issues"
22
+ },
23
+ "homepage": "https://github.com/VincenzoImp/create-academic-research#readme",
24
+ "publishConfig": {
25
+ "access": "public",
26
+ "provenance": true
27
+ },
28
+ "engines": {
29
+ "node": ">=20"
30
+ },
31
+ "bin": {
32
+ "create-academic-research": "dist/bin/create-academic-research.js",
33
+ "academic-research": "dist/bin/academic-research.js"
34
+ },
35
+ "files": [
36
+ "dist",
37
+ "template",
38
+ "README.md",
39
+ "LICENSE",
40
+ "SECURITY.md"
41
+ ],
42
+ "scripts": {
43
+ "clean": "rm -rf dist",
44
+ "build": "npm run clean && tsc",
45
+ "typecheck": "tsc --noEmit",
46
+ "test": "npm run build && node --test",
47
+ "lint": "npm run typecheck && node scripts/validate.mjs",
48
+ "release:check": "node scripts/check-release.mjs",
49
+ "prepare": "npm run build",
50
+ "audit:local": "npm test && npm run lint"
51
+ },
52
+ "dependencies": {
53
+ "yaml": "^2.7.0"
54
+ },
55
+ "devDependencies": {
56
+ "@types/node": "^22.19.19",
57
+ "typescript": "^5.9.3"
58
+ }
59
+ }
@@ -0,0 +1,47 @@
1
+ # Agent Operating Guide
2
+
3
+ This is an academic research project. Treat it as both a software project and a
4
+ scholarly record.
5
+
6
+ ## Core Rules
7
+
8
+ - Preserve source originals under `sources/`, `data/raw/`, and `data/external/`.
9
+ - Keep reusable logic in `src/`; keep notebooks for exploration and narrative output.
10
+ - Tie claims to sources, datasets, experiment records, or decision records.
11
+ - Update durable records when project knowledge changes.
12
+ - Keep large data, generated caches, credentials, and private review material out of git.
13
+
14
+ ## First Read
15
+
16
+ 1. `README.md`
17
+ 2. `configs/default.yaml`
18
+ 3. `configs/capabilities.yaml`
19
+ 4. `docs/agent/capability-profile.md`
20
+ 5. `docs/agent/research-program.md`
21
+ 6. `wiki/index.md`
22
+
23
+ ## Standard Workflow
24
+
25
+ 1. Identify whether the task affects sources, data, methods, experiments, claims, writing, or agent setup.
26
+ 2. Use project-local skills when available; route ambiguous work through `research-project-router`.
27
+ 3. Make repeatable changes in `src/`, `configs/`, or documented workflows.
28
+ 4. Add tests or validation for code and structural changes.
29
+ 5. Update `wiki/log.md` and affected wiki/docs pages before finishing.
30
+
31
+ ## Memory Contract
32
+
33
+ - `wiki/log.md` is chronological and append-only.
34
+ - `wiki/index.md` indexes reusable pages.
35
+ - `wiki/synthesis.md` changes only when the project-level interpretation changes.
36
+ - `wiki/open_questions.md` tracks unresolved questions.
37
+ - `wiki/contradictions.md` tracks conflicts across sources, data, or runs.
38
+
39
+ ## Evidence
40
+
41
+ - Native PDFs and reports go in `sources/pdfs/`.
42
+ - Derived Markdown goes in `sources/markdown/`.
43
+ - Metadata goes in `sources/metadata/`.
44
+ - Bibliography records go in `sources/bib/references.bib`.
45
+ - Citation issues go in `sources/bib/citation-audit.csv`.
46
+ - SOTA records go in `sota/`.
47
+ - Curated experiment records go in `experiments/`.