create-academic-research 0.1.4 → 0.1.6
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 +50 -14
- package/dist/src/agents.d.ts +9 -0
- package/dist/src/agents.js +126 -0
- package/dist/src/capabilities.d.ts +2 -2
- package/dist/src/capabilities.js +73 -19
- package/dist/src/cli.js +93 -19
- package/dist/src/project.js +4 -2
- package/dist/src/prompts.js +2 -1
- package/dist/src/stack.d.ts +8 -0
- package/dist/src/stack.js +127 -73
- package/package.json +1 -1
- package/template/README.md +14 -7
- package/template/package.json +2 -1
package/README.md
CHANGED
|
@@ -35,9 +35,10 @@ adjacent interdisciplinary CS.
|
|
|
35
35
|
|
|
36
36
|
The generated repository is agent-neutral. By default the wizard records
|
|
37
37
|
`agent: universal`, installs one shared project-local `.agents/skills` copy,
|
|
38
|
-
and writes generic MCP snippets. Use `--agent <
|
|
39
|
-
|
|
40
|
-
`
|
|
38
|
+
and writes generic MCP snippets. Use `--agent <id>` only when you want to force
|
|
39
|
+
a specific target recognized by the `skills` CLI. Run
|
|
40
|
+
`npx academic-research agents list` inside a generated project to see every
|
|
41
|
+
supported target and alias.
|
|
41
42
|
|
|
42
43
|
## Default Experience
|
|
43
44
|
|
|
@@ -45,10 +46,12 @@ By default, the wizard:
|
|
|
45
46
|
|
|
46
47
|
- creates the repository structure;
|
|
47
48
|
- installs the project-local `VincenzoImp/academic-research-skills` package;
|
|
48
|
-
- enables the
|
|
49
|
-
|
|
49
|
+
- enables only the low-friction `arxiv` MCP record;
|
|
50
|
+
- documents the wider MCP catalog, including Semantic Scholar, OpenAlex, DBLP,
|
|
51
|
+
PubMed, Zotero, Crossref, Overleaf, and fallback aggregators;
|
|
50
52
|
- writes `configs/capabilities.yaml`;
|
|
51
53
|
- writes `docs/agent/capability-profile.md`;
|
|
54
|
+
- writes `docs/agent/mcp-setup.md`;
|
|
52
55
|
- writes `docs/agent/generated/mcp.json` unless an explicit agent target is set;
|
|
53
56
|
- appends the onboarding event to `wiki/log.md`;
|
|
54
57
|
- does not install external MCP tools unless explicitly requested.
|
|
@@ -57,6 +60,12 @@ Use `--preset enhanced` when you also want the curated complementary external
|
|
|
57
60
|
skill bundles for agent engineering, frontend work, testing, document formats,
|
|
58
61
|
and PDF conversion.
|
|
59
62
|
|
|
63
|
+
Research-specific skills are intentionally not pulled from external packages by
|
|
64
|
+
default. The academic research workflow, literature review, citation audit,
|
|
65
|
+
paper writing, peer review, rebuttal, and reproduction policies come from
|
|
66
|
+
`VincenzoImp/academic-research-skills`. External skills installed by the wizard
|
|
67
|
+
are complementary tooling only.
|
|
68
|
+
|
|
60
69
|
## Non-Interactive Create
|
|
61
70
|
|
|
62
71
|
```bash
|
|
@@ -76,6 +85,7 @@ Inside a generated project:
|
|
|
76
85
|
```bash
|
|
77
86
|
npx academic-research doctor
|
|
78
87
|
npx academic-research rename --title "New Title" --slug new-title --package new_title
|
|
88
|
+
npx academic-research agents list
|
|
79
89
|
npx academic-research skills presets
|
|
80
90
|
npx academic-research skills install --preset default
|
|
81
91
|
npx academic-research skills install --preset enhanced
|
|
@@ -88,6 +98,7 @@ npx academic-research mcp list
|
|
|
88
98
|
npx academic-research mcp enabled
|
|
89
99
|
npx academic-research mcp available
|
|
90
100
|
npx academic-research mcp commands arxiv
|
|
101
|
+
npx academic-research mcp env openalex semantic-scholar zotero
|
|
91
102
|
npx academic-research mcp enable arxiv openalex
|
|
92
103
|
npx academic-research mcp disable arxiv
|
|
93
104
|
npx academic-research mcp install arxiv
|
|
@@ -116,11 +127,12 @@ MCP commands are split by side-effect:
|
|
|
116
127
|
| `mcp enabled` | List only enabled MCP server ids. |
|
|
117
128
|
| `mcp available` | List the local MCP catalog. |
|
|
118
129
|
| `mcp commands` | Print finite external install commands without running them. Runtime-only `uvx`/`npx` servers may have no install command. |
|
|
130
|
+
| `mcp env` | Print required/recommended env vars, hosted endpoints, local prerequisites, and setup commands for selected servers. |
|
|
119
131
|
| `mcp enable` | Enable an MCP server in project records and generated snippets. |
|
|
120
132
|
| `mcp disable` | Remove an MCP server from project records and generated snippets. |
|
|
121
133
|
| `mcp install` | Run finite external tool install commands for selected MCP servers. It must not launch stdio MCP servers. |
|
|
122
134
|
| `mcp uninstall` | Run the external uninstall command when one exists. |
|
|
123
|
-
| `mcp doctor` | Validate enabled MCP records and
|
|
135
|
+
| `mcp doctor` | Validate enabled MCP records, generated snippets, required env vars, and documented manual prerequisites. |
|
|
124
136
|
|
|
125
137
|
## Companion Skills
|
|
126
138
|
|
|
@@ -134,8 +146,9 @@ The create wizard can install that project-local package automatically.
|
|
|
134
146
|
Those skills are portable `SKILL.md` instructions, but they require an
|
|
135
147
|
agent/runtime that can load skills or include the relevant instructions in
|
|
136
148
|
context. They are not automatic capabilities of every raw model API.
|
|
137
|
-
Use `--agent <
|
|
138
|
-
`--agent claude
|
|
149
|
+
Use `--agent <id>` for explicit setup with any id from
|
|
150
|
+
`academic-research agents list`. The shorthand `--agent claude` is normalized
|
|
151
|
+
to the supported `claude-code` target.
|
|
139
152
|
Avoid `--agent auto` for unattended setup: the upstream `skills` CLI may expand
|
|
140
153
|
it to every agent it detects on the machine.
|
|
141
154
|
|
|
@@ -144,11 +157,34 @@ Preset intent:
|
|
|
144
157
|
| Preset | Intent |
|
|
145
158
|
|---|---|
|
|
146
159
|
| `minimal` | Academic research skills only, no MCP records. |
|
|
147
|
-
| `default` | Academic research skills plus
|
|
160
|
+
| `default` | Academic research skills plus the low-friction arXiv MCP record. |
|
|
148
161
|
| `enhanced` | `default` plus curated external complementary skill bundles. |
|
|
149
|
-
| `literature` | SOTA and systematic-review work with
|
|
150
|
-
| `writing` | Paper-writing
|
|
151
|
-
| `full` | Broad
|
|
162
|
+
| `literature` | SOTA and systematic-review work with arXiv plus DBLP for computer science bibliography. |
|
|
163
|
+
| `writing` | Paper-writing work; Overleaf is documented as an opt-in credentialed integration. |
|
|
164
|
+
| `full` | Broad setup with low-friction arXiv and DBLP records plus the full optional MCP catalog documented. |
|
|
165
|
+
|
|
166
|
+
MCP defaults are intentionally conservative. Semantic Scholar, OpenAlex,
|
|
167
|
+
Zotero, Overleaf, Crossref, and fallback aggregators are useful, but they need
|
|
168
|
+
API keys, local apps, manual setup, or source-policy review. Enable them with
|
|
169
|
+
`npx academic-research mcp enable <server>` after reading
|
|
170
|
+
`docs/agent/mcp-setup.md`, use `npx academic-research mcp env <server>` to see
|
|
171
|
+
runtime prerequisites, then run `npx academic-research mcp doctor`.
|
|
172
|
+
|
|
173
|
+
The MCP catalog distinguishes local runtime adapters from hosted endpoints and
|
|
174
|
+
manual integrations. arXiv and DBLP are low-friction local `uvx` runtimes.
|
|
175
|
+
Semantic Scholar is useful for citation graphs but works best with
|
|
176
|
+
`SEMANTIC_SCHOLAR_API_KEY`. OpenAlex requires `OPENALEX_API_KEY` for the
|
|
177
|
+
selected local adapter; OpenAlex keys are free for normal academic use with
|
|
178
|
+
daily free usage. PubMed is a biomedical-specific `npx` runtime and remains
|
|
179
|
+
opt-in. Zotero needs the local Zotero desktop app and Zoty setup. Overleaf is
|
|
180
|
+
manual and credentialed. Crossref and broad paper-search aggregators are kept
|
|
181
|
+
as fallback/manual entries until a project explicitly needs them.
|
|
182
|
+
|
|
183
|
+
Generated MCP snippets are project documentation and client-ready config, not
|
|
184
|
+
live tools by themselves. Your MCP client must load the generated snippet, and
|
|
185
|
+
the referenced commands must be available on your machine or runnable through
|
|
186
|
+
`uvx`/`npx`. `mcp install` only runs finite setup commands such as the arXiv
|
|
187
|
+
tool install; it deliberately does not launch stdio MCP servers.
|
|
152
188
|
|
|
153
189
|
## Validate This Package
|
|
154
190
|
|
|
@@ -166,8 +202,8 @@ Releases are tag-driven. Update `package.json` and `package-lock.json`, commit
|
|
|
166
202
|
the change, create `vX.Y.Z`, and push the tag:
|
|
167
203
|
|
|
168
204
|
```bash
|
|
169
|
-
git tag -a v0.1.
|
|
170
|
-
git push origin main v0.1.
|
|
205
|
+
git tag -a v0.1.6 -m "v0.1.6"
|
|
206
|
+
git push origin main v0.1.6
|
|
171
207
|
```
|
|
172
208
|
|
|
173
209
|
Once the GitHub repository is public, the release workflow validates the tag
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare const DEFAULT_AGENT = "universal";
|
|
2
|
+
export declare const AUTO_AGENT = "auto";
|
|
3
|
+
export declare const SUPPORTED_SKILL_AGENT_TARGETS: readonly ["adal", "aider-desk", "amp", "antigravity", "augment", "bob", "claude-code", "cline", "codearts-agent", "codebuddy", "codemaker", "codestudio", "codex", "command-code", "continue", "cortex", "crush", "cursor", "deepagents", "devin", "dexto", "droid", "firebender", "forgecode", "gemini-cli", "github-copilot", "goose", "hermes-agent", "iflow-cli", "junie", "kilo", "kimi-cli", "kiro-cli", "kode", "mcpjam", "mistral-vibe", "mux", "neovate", "openclaw", "opencode", "openhands", "pi", "pochi", "qoder", "qwen-code", "replit", "roo", "rovodev", "tabnine-cli", "trae", "trae-cn", "universal", "warp", "windsurf", "zencoder"];
|
|
4
|
+
export declare const AGENT_TARGET_ALIASES: Record<string, string>;
|
|
5
|
+
export declare function normalizeAgentTarget(agent: string | undefined): string;
|
|
6
|
+
export declare function assertKnownAgentTarget(agent: string | undefined): string;
|
|
7
|
+
export declare function formatAgentTargetList(): string;
|
|
8
|
+
export declare function formatSupportedAgentTargetLines(indent?: string, width?: number): string[];
|
|
9
|
+
export declare function formatAgentAliasLines(indent?: string): string[];
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
export const DEFAULT_AGENT = "universal";
|
|
2
|
+
export const AUTO_AGENT = "auto";
|
|
3
|
+
export const SUPPORTED_SKILL_AGENT_TARGETS = [
|
|
4
|
+
"adal",
|
|
5
|
+
"aider-desk",
|
|
6
|
+
"amp",
|
|
7
|
+
"antigravity",
|
|
8
|
+
"augment",
|
|
9
|
+
"bob",
|
|
10
|
+
"claude-code",
|
|
11
|
+
"cline",
|
|
12
|
+
"codearts-agent",
|
|
13
|
+
"codebuddy",
|
|
14
|
+
"codemaker",
|
|
15
|
+
"codestudio",
|
|
16
|
+
"codex",
|
|
17
|
+
"command-code",
|
|
18
|
+
"continue",
|
|
19
|
+
"cortex",
|
|
20
|
+
"crush",
|
|
21
|
+
"cursor",
|
|
22
|
+
"deepagents",
|
|
23
|
+
"devin",
|
|
24
|
+
"dexto",
|
|
25
|
+
"droid",
|
|
26
|
+
"firebender",
|
|
27
|
+
"forgecode",
|
|
28
|
+
"gemini-cli",
|
|
29
|
+
"github-copilot",
|
|
30
|
+
"goose",
|
|
31
|
+
"hermes-agent",
|
|
32
|
+
"iflow-cli",
|
|
33
|
+
"junie",
|
|
34
|
+
"kilo",
|
|
35
|
+
"kimi-cli",
|
|
36
|
+
"kiro-cli",
|
|
37
|
+
"kode",
|
|
38
|
+
"mcpjam",
|
|
39
|
+
"mistral-vibe",
|
|
40
|
+
"mux",
|
|
41
|
+
"neovate",
|
|
42
|
+
"openclaw",
|
|
43
|
+
"opencode",
|
|
44
|
+
"openhands",
|
|
45
|
+
"pi",
|
|
46
|
+
"pochi",
|
|
47
|
+
"qoder",
|
|
48
|
+
"qwen-code",
|
|
49
|
+
"replit",
|
|
50
|
+
"roo",
|
|
51
|
+
"rovodev",
|
|
52
|
+
"tabnine-cli",
|
|
53
|
+
"trae",
|
|
54
|
+
"trae-cn",
|
|
55
|
+
"universal",
|
|
56
|
+
"warp",
|
|
57
|
+
"windsurf",
|
|
58
|
+
"zencoder"
|
|
59
|
+
];
|
|
60
|
+
export const AGENT_TARGET_ALIASES = {
|
|
61
|
+
claude: "claude-code",
|
|
62
|
+
claude_code: "claude-code"
|
|
63
|
+
};
|
|
64
|
+
const SUPPORTED_AGENT_TARGETS = new Set([
|
|
65
|
+
AUTO_AGENT,
|
|
66
|
+
...SUPPORTED_SKILL_AGENT_TARGETS
|
|
67
|
+
]);
|
|
68
|
+
export function normalizeAgentTarget(agent) {
|
|
69
|
+
const value = agent?.trim();
|
|
70
|
+
if (!value)
|
|
71
|
+
return DEFAULT_AGENT;
|
|
72
|
+
const normalized = value.toLowerCase();
|
|
73
|
+
return AGENT_TARGET_ALIASES[normalized] ?? normalized;
|
|
74
|
+
}
|
|
75
|
+
export function assertKnownAgentTarget(agent) {
|
|
76
|
+
const normalized = normalizeAgentTarget(agent);
|
|
77
|
+
if (!SUPPORTED_AGENT_TARGETS.has(normalized)) {
|
|
78
|
+
const value = agent?.trim() || DEFAULT_AGENT;
|
|
79
|
+
throw new Error([
|
|
80
|
+
`unknown agent target: ${value}`,
|
|
81
|
+
`Use ${DEFAULT_AGENT}, ${AUTO_AGENT}, or one supported skills.sh agent id.`,
|
|
82
|
+
"List targets with: npx -p create-academic-research academic-research agents list",
|
|
83
|
+
`Supported ids: ${specificAgentTargets().join(", ")}`,
|
|
84
|
+
`Aliases: ${formatAgentAliasesInline()}`
|
|
85
|
+
].join("\n"));
|
|
86
|
+
}
|
|
87
|
+
return normalized;
|
|
88
|
+
}
|
|
89
|
+
export function formatAgentTargetList() {
|
|
90
|
+
const lines = [
|
|
91
|
+
`${DEFAULT_AGENT}\tRecommended shared project-local .agents/skills copy`,
|
|
92
|
+
`${AUTO_AGENT}\tLet the skills CLI detect installed agents; may create multiple agent-specific copies`,
|
|
93
|
+
...specificAgentTargets().map((agent) => `${agent}\tskills.sh agent id`),
|
|
94
|
+
...Object.entries(AGENT_TARGET_ALIASES).map(([alias, target]) => `alias\t${alias}\t${target}`)
|
|
95
|
+
];
|
|
96
|
+
return `${lines.join("\n")}\n`;
|
|
97
|
+
}
|
|
98
|
+
export function formatSupportedAgentTargetLines(indent = " ", width = 100) {
|
|
99
|
+
const labels = specificAgentTargets();
|
|
100
|
+
const lines = [];
|
|
101
|
+
let current = indent;
|
|
102
|
+
for (const label of labels) {
|
|
103
|
+
const next = current === indent ? label : `, ${label}`;
|
|
104
|
+
if (current.length + next.length > width) {
|
|
105
|
+
lines.push(current);
|
|
106
|
+
current = `${indent}${label}`;
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
current += next;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (current.trim())
|
|
113
|
+
lines.push(current);
|
|
114
|
+
return lines;
|
|
115
|
+
}
|
|
116
|
+
export function formatAgentAliasLines(indent = " ") {
|
|
117
|
+
return Object.entries(AGENT_TARGET_ALIASES).map(([alias, target]) => `${indent}${alias} -> ${target}`);
|
|
118
|
+
}
|
|
119
|
+
function formatAgentAliasesInline() {
|
|
120
|
+
return Object.entries(AGENT_TARGET_ALIASES)
|
|
121
|
+
.map(([alias, target]) => `${alias} -> ${target}`)
|
|
122
|
+
.join(", ");
|
|
123
|
+
}
|
|
124
|
+
function specificAgentTargets() {
|
|
125
|
+
return SUPPORTED_SKILL_AGENT_TARGETS.filter((agent) => agent !== DEFAULT_AGENT);
|
|
126
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { DEFAULT_AGENT, SUPPORTED_SKILL_AGENT_TARGETS } from "./agents.js";
|
|
1
2
|
import { type Runner } from "./runner.js";
|
|
2
3
|
import { type McpToolCommandKey } from "./stack.js";
|
|
3
|
-
export
|
|
4
|
+
export { DEFAULT_AGENT, SUPPORTED_SKILL_AGENT_TARGETS };
|
|
4
5
|
export interface CapabilityState {
|
|
5
6
|
agent: string;
|
|
6
7
|
preset: string;
|
|
@@ -51,4 +52,3 @@ export declare function installMcpTools(root: string, servers: string[], runner?
|
|
|
51
52
|
export declare function uninstallMcpTools(root: string, servers: string[], runner?: Runner): Promise<CapabilityCommandResult>;
|
|
52
53
|
export declare function doctorMcpServers(root: string): Promise<McpDoctorResult>;
|
|
53
54
|
export declare function assertKnownMcpServers(servers: string[]): void;
|
|
54
|
-
export {};
|
package/dist/src/capabilities.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { appendFile, mkdir, readdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
2
2
|
import { join, relative } from "node:path";
|
|
3
3
|
import YAML from "yaml";
|
|
4
|
+
import { assertKnownAgentTarget, AUTO_AGENT, DEFAULT_AGENT, normalizeAgentTarget, SUPPORTED_SKILL_AGENT_TARGETS } from "./agents.js";
|
|
4
5
|
import { defaultRunner } from "./runner.js";
|
|
5
6
|
import { AGENT_STACK, presetMcpServers } from "./stack.js";
|
|
6
|
-
export
|
|
7
|
-
const AUTO_AGENT = "auto";
|
|
7
|
+
export { DEFAULT_AGENT, SUPPORTED_SKILL_AGENT_TARGETS };
|
|
8
8
|
export async function readCapabilities(root) {
|
|
9
9
|
try {
|
|
10
10
|
return readCapabilitiesFile(root);
|
|
@@ -18,13 +18,14 @@ export async function readCapabilities(root) {
|
|
|
18
18
|
}
|
|
19
19
|
export async function writeCapabilities(root, state) {
|
|
20
20
|
const next = {
|
|
21
|
-
agent:
|
|
21
|
+
agent: assertKnownAgentTarget(state.agent),
|
|
22
22
|
preset: state.preset ?? "default",
|
|
23
23
|
scope: "project-local",
|
|
24
24
|
mcp_servers: [...(state.mcp_servers ?? [])]
|
|
25
25
|
};
|
|
26
26
|
await writeFile(join(root, "configs/capabilities.yaml"), YAML.stringify(next), "utf8");
|
|
27
27
|
await writeCapabilityProfile(root, next);
|
|
28
|
+
await writeMcpSetup(root, next);
|
|
28
29
|
await writeMcpSnippet(root, next);
|
|
29
30
|
await appendCapabilityLog(root, next);
|
|
30
31
|
}
|
|
@@ -42,7 +43,7 @@ export async function buildSkillInstallCommands(root, preset = "default", option
|
|
|
42
43
|
if (!selected)
|
|
43
44
|
throw new Error(`unknown skill preset: ${preset}`);
|
|
44
45
|
const state = await readCapabilities(root);
|
|
45
|
-
const agent =
|
|
46
|
+
const agent = assertKnownAgentTarget(options.agent ?? state.agent);
|
|
46
47
|
const commands = [];
|
|
47
48
|
for (const bundleName of selected.skill_bundles) {
|
|
48
49
|
const bundle = AGENT_STACK.skill_bundles[bundleName];
|
|
@@ -61,7 +62,7 @@ export async function buildSkillInstallCommands(root, preset = "default", option
|
|
|
61
62
|
}
|
|
62
63
|
export async function installSkills(root, preset = "default", options = {}, runner = defaultRunner) {
|
|
63
64
|
const state = await readCapabilities(root);
|
|
64
|
-
const agent =
|
|
65
|
+
const agent = assertKnownAgentTarget(options.agent ?? state.agent);
|
|
65
66
|
const commands = await buildSkillInstallCommands(root, preset, options);
|
|
66
67
|
for (const command of commands) {
|
|
67
68
|
await runner.run(command, { cwd: root });
|
|
@@ -135,7 +136,7 @@ export async function enableMcpServers(root, servers, options = {}) {
|
|
|
135
136
|
const selected = dedupe([...(state.mcp_servers ?? []), ...servers]);
|
|
136
137
|
await writeCapabilities(root, {
|
|
137
138
|
...state,
|
|
138
|
-
agent: options.agent ?? state.agent,
|
|
139
|
+
agent: assertKnownAgentTarget(options.agent ?? state.agent),
|
|
139
140
|
mcp_servers: selected
|
|
140
141
|
});
|
|
141
142
|
return { ok: true, servers: selected };
|
|
@@ -147,7 +148,7 @@ export async function disableMcpServers(root, servers, options = {}) {
|
|
|
147
148
|
const selected = (state.mcp_servers ?? []).filter((server) => !blocked.has(server));
|
|
148
149
|
await writeCapabilities(root, {
|
|
149
150
|
...state,
|
|
150
|
-
agent: options.agent ?? state.agent,
|
|
151
|
+
agent: assertKnownAgentTarget(options.agent ?? state.agent),
|
|
151
152
|
mcp_servers: selected
|
|
152
153
|
});
|
|
153
154
|
return { ok: true, servers: selected };
|
|
@@ -233,6 +234,19 @@ export async function doctorMcpServers(root) {
|
|
|
233
234
|
const server = AGENT_STACK.mcp_servers[name];
|
|
234
235
|
if (!server)
|
|
235
236
|
continue;
|
|
237
|
+
for (const envName of server.required_env) {
|
|
238
|
+
if (!process.env[envName]) {
|
|
239
|
+
errors.push(`${name}: missing required environment variable: ${envName}`);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
for (const envName of server.recommended_env) {
|
|
243
|
+
if (!process.env[envName]) {
|
|
244
|
+
warnings.push(`${name}: recommended environment variable not set: ${envName}`);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
if (server.local_service) {
|
|
248
|
+
warnings.push(`${name}: requires local service: ${server.local_service}`);
|
|
249
|
+
}
|
|
236
250
|
if (!server.command) {
|
|
237
251
|
warnings.push(`${name}: manual setup only; no generated client command`);
|
|
238
252
|
continue;
|
|
@@ -240,9 +254,6 @@ export async function doctorMcpServers(root) {
|
|
|
240
254
|
if (!generatedServers.has(name)) {
|
|
241
255
|
errors.push(`${name}: enabled but missing from generated MCP snippet`);
|
|
242
256
|
}
|
|
243
|
-
if (!server.install_command) {
|
|
244
|
-
warnings.push(`${name}: no automated install command is defined`);
|
|
245
|
-
}
|
|
246
257
|
}
|
|
247
258
|
return { ok: errors.length === 0, errors, warnings, enabled };
|
|
248
259
|
}
|
|
@@ -269,7 +280,7 @@ async function writeCapabilityProfile(root, state) {
|
|
|
269
280
|
const lines = [
|
|
270
281
|
"# Agent Capability Profile",
|
|
271
282
|
"",
|
|
272
|
-
`- Agent target: \`${
|
|
283
|
+
`- Agent target: \`${assertKnownAgentTarget(state.agent)}\``,
|
|
273
284
|
`- Preset: \`${state.preset ?? "default"}\``,
|
|
274
285
|
"- Scope: `project-local`",
|
|
275
286
|
"",
|
|
@@ -290,13 +301,46 @@ async function writeCapabilityProfile(root, state) {
|
|
|
290
301
|
else {
|
|
291
302
|
for (const name of state.mcp_servers) {
|
|
292
303
|
const server = AGENT_STACK.mcp_servers[name];
|
|
293
|
-
const status = server?.command ?
|
|
304
|
+
const status = server?.command ? server.execution_mode : "manual setup";
|
|
294
305
|
lines.push(`- \`${name}\` (${status}): ${server?.smoke_test ?? "Smoke-test before use."}`);
|
|
295
306
|
}
|
|
296
307
|
}
|
|
297
308
|
lines.push("", "## Rules", "", "- Skill installation is project-local by default.", "- Agent target `universal` installs one shared project-local `.agents/skills` copy.", "- MCP enable/disable changes project records; install/uninstall changes external tools.", "- Keep API keys, tokens, cookies, and browser sessions out of git.", "- Cite repository source records, not raw MCP output alone.", "");
|
|
298
309
|
await writeFile(join(root, "docs/agent/capability-profile.md"), lines.join("\n"), "utf8");
|
|
299
310
|
}
|
|
311
|
+
async function writeMcpSetup(root, state) {
|
|
312
|
+
const enabled = new Set(state.mcp_servers ?? []);
|
|
313
|
+
const lines = [
|
|
314
|
+
"# MCP Setup",
|
|
315
|
+
"",
|
|
316
|
+
"This file is generated from the project-local academic research capability stack.",
|
|
317
|
+
"MCP records are configuration snippets and setup guidance; the active MCP client must load the generated snippet before these servers become live tools.",
|
|
318
|
+
"",
|
|
319
|
+
"## Enabled MCP Servers",
|
|
320
|
+
""
|
|
321
|
+
];
|
|
322
|
+
if (enabled.size === 0) {
|
|
323
|
+
lines.push("- None.");
|
|
324
|
+
}
|
|
325
|
+
else {
|
|
326
|
+
for (const name of state.mcp_servers) {
|
|
327
|
+
const server = AGENT_STACK.mcp_servers[name];
|
|
328
|
+
if (!server)
|
|
329
|
+
continue;
|
|
330
|
+
lines.push(`- \`${name}\` (${server.readiness}, ${server.priority}): ${server.source_need}`, ` - Source: \`${server.source}\``, ` - Execution mode: \`${server.execution_mode}\``, ...(server.hosted_url ? [` - Hosted endpoint: <${server.hosted_url}>`] : []), ` - Runtime: ${formatRuntime(server.command, server.args)}`, ` - Install command: ${server.install_command ? `\`${server.install_command}\`` : "none; runtime-only or manual setup"}`, ...server.setup_commands.map((command) => ` - Setup command: \`${command}\``), ` - Smoke test: ${server.smoke_test}`, ` - Risks: ${server.risks}`);
|
|
331
|
+
appendMcpPrerequisiteLines(lines, server.required_env, server.recommended_env, server.local_service);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
lines.push("", "## Available MCP Catalog", "");
|
|
335
|
+
for (const [name, server] of Object.entries(AGENT_STACK.mcp_servers)) {
|
|
336
|
+
const status = enabled.has(name) ? "enabled" : "available";
|
|
337
|
+
lines.push(`- \`${name}\` (${status}, ${server.readiness}, ${server.priority}): ${server.source_need}`, ` - Source: \`${server.source}\``, ` - Execution mode: \`${server.execution_mode}\``, ...(server.hosted_url ? [` - Hosted endpoint: <${server.hosted_url}>`] : []), ...server.setup_commands.map((command) => ` - Setup command: \`${command}\``));
|
|
338
|
+
appendMcpPrerequisiteLines(lines, server.required_env, server.recommended_env, server.local_service);
|
|
339
|
+
}
|
|
340
|
+
lines.push("", "## Operating Rules", "", "- Keep secrets in your shell, MCP client secret store, or local untracked files; do not commit tokens or API keys.", "- Prefer the smallest enabled MCP set that covers the current research question.", "- Treat MCP output as retrieval metadata. Promote claims into repository source records only after source ingestion and citation audit.", "- Run `npx academic-research mcp doctor` after changing MCP records or environment variables.", "");
|
|
341
|
+
await mkdir(join(root, "docs/agent"), { recursive: true });
|
|
342
|
+
await writeFile(join(root, "docs/agent/mcp-setup.md"), lines.join("\n"), "utf8");
|
|
343
|
+
}
|
|
300
344
|
async function appendCapabilityLog(root, state) {
|
|
301
345
|
const logPath = join(root, "wiki/log.md");
|
|
302
346
|
const date = new Date().toISOString().slice(0, 10);
|
|
@@ -307,10 +351,24 @@ function dedupe(values) {
|
|
|
307
351
|
return [...new Set(values)];
|
|
308
352
|
}
|
|
309
353
|
function renderSkillCommand(command, agent) {
|
|
310
|
-
const normalized =
|
|
354
|
+
const normalized = assertKnownAgentTarget(agent);
|
|
311
355
|
const agentFlag = normalized === AUTO_AGENT ? "" : `--agent '${normalized}'`;
|
|
312
356
|
return command.replaceAll("{agent_flag}", agentFlag).replaceAll("{agent}", normalized);
|
|
313
357
|
}
|
|
358
|
+
function appendMcpPrerequisiteLines(lines, requiredEnv, recommendedEnv, localService) {
|
|
359
|
+
if (requiredEnv.length > 0)
|
|
360
|
+
lines.push(` - Requires env: ${requiredEnv.map((name) => `\`${name}\``).join(", ")}`);
|
|
361
|
+
if (recommendedEnv.length > 0) {
|
|
362
|
+
lines.push(` - Recommended env: ${recommendedEnv.map((name) => `\`${name}\``).join(", ")}`);
|
|
363
|
+
}
|
|
364
|
+
if (localService)
|
|
365
|
+
lines.push(` - Local prerequisite: ${localService}`);
|
|
366
|
+
}
|
|
367
|
+
function formatRuntime(command, args) {
|
|
368
|
+
if (!command)
|
|
369
|
+
return "manual setup";
|
|
370
|
+
return `\`${[command, ...args].join(" ")}\``;
|
|
371
|
+
}
|
|
314
372
|
async function readCapabilitiesFile(root) {
|
|
315
373
|
const path = join(root, "configs/capabilities.yaml");
|
|
316
374
|
return normalizeCapabilityState(YAML.parse(await readFile(path, "utf8")));
|
|
@@ -352,7 +410,7 @@ function splitCommand(command) {
|
|
|
352
410
|
function normalizeCapabilityState(value) {
|
|
353
411
|
const record = typeof value === "object" && value !== null ? value : {};
|
|
354
412
|
return {
|
|
355
|
-
agent:
|
|
413
|
+
agent: assertKnownAgentTarget(typeof record.agent === "string" ? record.agent : undefined),
|
|
356
414
|
preset: typeof record.preset === "string" ? record.preset : "default",
|
|
357
415
|
scope: "project-local",
|
|
358
416
|
mcp_servers: Array.isArray(record.mcp_servers)
|
|
@@ -397,12 +455,8 @@ async function removeSkillsFromLock(root, skills) {
|
|
|
397
455
|
await writeFile(path, `${JSON.stringify(record, null, 2)}\n`, "utf8");
|
|
398
456
|
}
|
|
399
457
|
}
|
|
400
|
-
function normalizeAgent(agent) {
|
|
401
|
-
const value = agent?.trim();
|
|
402
|
-
return value ? value : DEFAULT_AGENT;
|
|
403
|
-
}
|
|
404
458
|
function mcpSnippetFileName(agent) {
|
|
405
|
-
const normalized =
|
|
459
|
+
const normalized = normalizeAgentTarget(agent);
|
|
406
460
|
return normalized === DEFAULT_AGENT || normalized === AUTO_AGENT ? "mcp.json" : `${normalized}-mcp.json`;
|
|
407
461
|
}
|
|
408
462
|
async function removeInactiveMcpSnippets(outputDir, activeFile) {
|
package/dist/src/cli.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
import { basename, dirname, join, resolve } from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
|
-
import { disableMcpServers, doctorMcpServers, enableMcpServers, DEFAULT_AGENT, installMcpTools, installSkills, listInstalledSkills, mcpToolCommandTexts, readCapabilities, removeSkills, uninstallMcpTools, updateSkills } from "./capabilities.js";
|
|
4
|
+
import { assertKnownMcpServers, disableMcpServers, doctorMcpServers, enableMcpServers, DEFAULT_AGENT, installMcpTools, installSkills, listInstalledSkills, mcpToolCommandTexts, readCapabilities, removeSkills, uninstallMcpTools, updateSkills } from "./capabilities.js";
|
|
5
5
|
import { createProject, doctorProject, renameProject } from "./project.js";
|
|
6
6
|
import { askCreateOptions } from "./prompts.js";
|
|
7
7
|
import { AGENT_STACK, presetMcpServers } from "./stack.js";
|
|
8
|
+
import { formatAgentAliasLines, formatAgentTargetList, formatSupportedAgentTargetLines } from "./agents.js";
|
|
8
9
|
import { packageify, slugify, titleFromSlug } from "./names.js";
|
|
9
10
|
const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "../..");
|
|
10
11
|
const packageVersion = readPackageVersion();
|
|
@@ -101,6 +102,8 @@ async function lifecycleMain(argv) {
|
|
|
101
102
|
return doctorCommand(argv.slice(1));
|
|
102
103
|
if (command === "rename")
|
|
103
104
|
return renameCommand(argv.slice(1));
|
|
105
|
+
if (command === "agents")
|
|
106
|
+
return agentsCommand(argv.slice(1));
|
|
104
107
|
if (command === "skills")
|
|
105
108
|
return skillsCommand(argv.slice(1));
|
|
106
109
|
if (command === "mcp")
|
|
@@ -137,6 +140,21 @@ async function renameCommand(argv) {
|
|
|
137
140
|
console.log(`Renamed project to ${result.slug}`);
|
|
138
141
|
return 0;
|
|
139
142
|
}
|
|
143
|
+
async function agentsCommand(argv) {
|
|
144
|
+
const subcommand = argv[0] ?? "list";
|
|
145
|
+
const parsed = parseFlags(argv.slice(1), ROOT_FLAGS);
|
|
146
|
+
if (subcommand === "help" || subcommand === "--help" || subcommand === "-h" || flagBool(parsed.flags, "help")) {
|
|
147
|
+
printAgentsHelp();
|
|
148
|
+
return 0;
|
|
149
|
+
}
|
|
150
|
+
if (subcommand === "list") {
|
|
151
|
+
assertOnlyOptions(parsed.flags, "agents list", []);
|
|
152
|
+
assertNoArguments(parsed.positionals, "agents list");
|
|
153
|
+
process.stdout.write(formatAgentTargetList());
|
|
154
|
+
return 0;
|
|
155
|
+
}
|
|
156
|
+
throw new Error(`unknown agents command: ${subcommand}`);
|
|
157
|
+
}
|
|
140
158
|
async function skillsCommand(argv) {
|
|
141
159
|
const subcommand = argv[0] ?? "list";
|
|
142
160
|
const parsed = parseFlags(argv.slice(1), SKILLS_FLAGS);
|
|
@@ -226,10 +244,10 @@ async function mcpCommand(argv) {
|
|
|
226
244
|
assertNoArguments(parsed.positionals, "mcp list");
|
|
227
245
|
const state = await readCapabilities(root);
|
|
228
246
|
const enabled = new Set(state.mcp_servers ?? []);
|
|
247
|
+
console.log("status\tid\treadiness\texecution_mode\tdescription");
|
|
229
248
|
for (const [name, server] of Object.entries(AGENT_STACK.mcp_servers)) {
|
|
230
249
|
const status = enabled.has(name) ? "enabled" : "available";
|
|
231
|
-
|
|
232
|
-
console.log(`${status}\t${name}\t${server.source_need}\t${installer}`);
|
|
250
|
+
console.log(`${status}\t${name}\t${server.readiness}\t${server.execution_mode}\t${server.source_need}`);
|
|
233
251
|
}
|
|
234
252
|
return 0;
|
|
235
253
|
}
|
|
@@ -245,9 +263,9 @@ async function mcpCommand(argv) {
|
|
|
245
263
|
if (subcommand === "available") {
|
|
246
264
|
assertOnlyOptions(parsed.flags, "mcp available", []);
|
|
247
265
|
assertNoArguments(parsed.positionals, "mcp available");
|
|
266
|
+
console.log("id\treadiness\texecution_mode\tdescription");
|
|
248
267
|
for (const [name, server] of Object.entries(AGENT_STACK.mcp_servers)) {
|
|
249
|
-
|
|
250
|
-
console.log(`${name}\t${server.source_need}\t${installer}`);
|
|
268
|
+
console.log(`${name}\t${server.readiness}\t${server.execution_mode}\t${server.source_need}`);
|
|
251
269
|
}
|
|
252
270
|
return 0;
|
|
253
271
|
}
|
|
@@ -260,6 +278,15 @@ async function mcpCommand(argv) {
|
|
|
260
278
|
console.log(command);
|
|
261
279
|
return 0;
|
|
262
280
|
}
|
|
281
|
+
if (subcommand === "env") {
|
|
282
|
+
assertOnlyOptions(parsed.flags, "mcp env", ["root"]);
|
|
283
|
+
const root = resolve(flagString(parsed.flags, "root") ?? ".");
|
|
284
|
+
const selected = parsed.positionals.length > 0 ? parsed.positionals : (await readCapabilities(root)).mcp_servers;
|
|
285
|
+
assertKnownMcpServers(selected);
|
|
286
|
+
console.log("id\ttype\tvalue");
|
|
287
|
+
printMcpEnvironment(selected);
|
|
288
|
+
return 0;
|
|
289
|
+
}
|
|
263
290
|
if (subcommand === "enable") {
|
|
264
291
|
assertOnlyOptions(parsed.flags, "mcp enable", ["root", "agent"]);
|
|
265
292
|
const root = resolve(flagString(parsed.flags, "root") ?? ".");
|
|
@@ -387,11 +414,6 @@ function assertOnlyOptions(flags, command, allowedOptions) {
|
|
|
387
414
|
throw new Error(`${command} does not accept ${unexpected.map((name) => `--${name}`).join(", ")}`);
|
|
388
415
|
}
|
|
389
416
|
}
|
|
390
|
-
function mcpInstallMode(installCommand, runtimeCommand) {
|
|
391
|
-
if (installCommand)
|
|
392
|
-
return installCommand;
|
|
393
|
-
return runtimeCommand ? "runtime-only" : "manual";
|
|
394
|
-
}
|
|
395
417
|
export function formatInteractiveCreateGuide() {
|
|
396
418
|
const presetLines = Object.entries(AGENT_STACK.presets).map(([name, preset]) => ` ${name.padEnd(10)} ${preset.description}`);
|
|
397
419
|
return [
|
|
@@ -401,15 +423,24 @@ export function formatInteractiveCreateGuide() {
|
|
|
401
423
|
...presetLines,
|
|
402
424
|
"",
|
|
403
425
|
"Agent target:",
|
|
404
|
-
" universal
|
|
405
|
-
"
|
|
406
|
-
"
|
|
426
|
+
" universal Recommended. One shared project-local .agents/skills copy.",
|
|
427
|
+
" auto Let the skills CLI detect installed agents; may create multiple agent-specific copies.",
|
|
428
|
+
" <id> Any supported skills.sh agent id.",
|
|
429
|
+
"",
|
|
430
|
+
"Supported specific agent ids:",
|
|
431
|
+
...formatSupportedAgentTargetLines(),
|
|
432
|
+
"",
|
|
433
|
+
"Aliases:",
|
|
434
|
+
...formatAgentAliasLines(),
|
|
407
435
|
"",
|
|
408
436
|
"Skill and MCP behavior:",
|
|
409
437
|
" Skills are copied into the project, not installed globally.",
|
|
410
438
|
" MCP records are written into configs/capabilities.yaml and docs/agent/generated/.",
|
|
439
|
+
" docs/agent/mcp-setup.md records enabled servers, optional catalog entries, env vars, and smoke tests.",
|
|
440
|
+
" default enables only low-friction arXiv; credentialed/local services are opt-in.",
|
|
411
441
|
" MCP installers are optional and run only finite installer commands.",
|
|
412
|
-
"
|
|
442
|
+
" MCP execution modes are explicit: uvx-runtime, npx-runtime, local-service, manual, or fallback.",
|
|
443
|
+
" Use `academic-research mcp env <server>` to inspect env vars and local prerequisites.",
|
|
413
444
|
""
|
|
414
445
|
].join("\n");
|
|
415
446
|
}
|
|
@@ -426,7 +457,7 @@ function printCreateHelp() {
|
|
|
426
457
|
" --package <name> Python package name. Default: normalized project name.",
|
|
427
458
|
" --preset <name> Capability preset: minimal, default, enhanced, literature, writing, full.",
|
|
428
459
|
" --profile <name> Project profile metadata. Default: academic-general.",
|
|
429
|
-
" --agent <
|
|
460
|
+
" --agent <id> Agent target: universal, auto, or a supported skills.sh id.",
|
|
430
461
|
" --install-skills Install project-local skills without prompting.",
|
|
431
462
|
" --no-install-skills Skip project-local skill installation.",
|
|
432
463
|
" --install-mcp-tools Run finite external MCP install commands after creation.",
|
|
@@ -445,7 +476,7 @@ function printMissingTargetHelp() {
|
|
|
445
476
|
}
|
|
446
477
|
function printLifecycleHelp() {
|
|
447
478
|
console.log([
|
|
448
|
-
"Usage: academic-research <doctor|rename|skills|mcp>",
|
|
479
|
+
"Usage: academic-research <doctor|rename|agents|skills|mcp>",
|
|
449
480
|
"",
|
|
450
481
|
"Manage a generated academic research repository after creation.",
|
|
451
482
|
"",
|
|
@@ -454,6 +485,21 @@ function printLifecycleHelp() {
|
|
|
454
485
|
" -v, --version Show package version."
|
|
455
486
|
].join("\n"));
|
|
456
487
|
}
|
|
488
|
+
function printAgentsHelp() {
|
|
489
|
+
console.log([
|
|
490
|
+
"Usage: academic-research agents <list>",
|
|
491
|
+
"",
|
|
492
|
+
"List supported project-local agent targets.",
|
|
493
|
+
"",
|
|
494
|
+
"Targets:",
|
|
495
|
+
" universal Recommended shared project-local .agents/skills copy.",
|
|
496
|
+
" auto Let the skills CLI detect installed agents.",
|
|
497
|
+
" <id> A supported skills.sh agent id.",
|
|
498
|
+
"",
|
|
499
|
+
"Options:",
|
|
500
|
+
" -h, --help Show this help."
|
|
501
|
+
].join("\n"));
|
|
502
|
+
}
|
|
457
503
|
function printSkillsHelp() {
|
|
458
504
|
console.log([
|
|
459
505
|
"Usage: academic-research skills <list|status|presets|install|remove|uninstall|update> [options]",
|
|
@@ -463,22 +509,50 @@ function printSkillsHelp() {
|
|
|
463
509
|
"Options:",
|
|
464
510
|
" --root <path> Project root for list, status, install, remove, uninstall, update.",
|
|
465
511
|
" --preset <name> Capability preset for install.",
|
|
466
|
-
" --agent <
|
|
512
|
+
" --agent <id> Agent selector for install. Default: project capability agent.",
|
|
467
513
|
" -h, --help Show this help."
|
|
468
514
|
].join("\n"));
|
|
469
515
|
}
|
|
470
516
|
function printMcpHelp() {
|
|
471
517
|
console.log([
|
|
472
|
-
"Usage: academic-research mcp <list|enabled|available|commands|enable|disable|install|uninstall|doctor> [servers...]",
|
|
518
|
+
"Usage: academic-research mcp <list|enabled|available|commands|env|enable|disable|install|uninstall|doctor> [servers...]",
|
|
473
519
|
"",
|
|
474
520
|
"Manage MCP records and finite external MCP tool installs.",
|
|
475
521
|
"",
|
|
476
522
|
"Options:",
|
|
477
523
|
" --root <path> Project root for project-state commands.",
|
|
478
|
-
" --agent <
|
|
524
|
+
" --agent <id> Agent for enable/disable generated snippets.",
|
|
479
525
|
" -h, --help Show this help."
|
|
480
526
|
].join("\n"));
|
|
481
527
|
}
|
|
528
|
+
function printMcpEnvironment(servers) {
|
|
529
|
+
for (const name of servers) {
|
|
530
|
+
const server = AGENT_STACK.mcp_servers[name];
|
|
531
|
+
let wroteLine = false;
|
|
532
|
+
for (const envName of server.required_env) {
|
|
533
|
+
console.log(`${name}\trequired\t${envName}`);
|
|
534
|
+
wroteLine = true;
|
|
535
|
+
}
|
|
536
|
+
for (const envName of server.recommended_env) {
|
|
537
|
+
console.log(`${name}\trecommended\t${envName}`);
|
|
538
|
+
wroteLine = true;
|
|
539
|
+
}
|
|
540
|
+
if (server.hosted_url) {
|
|
541
|
+
console.log(`${name}\thosted-endpoint\t${server.hosted_url}`);
|
|
542
|
+
wroteLine = true;
|
|
543
|
+
}
|
|
544
|
+
if (server.local_service) {
|
|
545
|
+
console.log(`${name}\tlocal-service\t${server.local_service}`);
|
|
546
|
+
wroteLine = true;
|
|
547
|
+
}
|
|
548
|
+
for (const command of server.setup_commands) {
|
|
549
|
+
console.log(`${name}\tsetup-command\t${command}`);
|
|
550
|
+
wroteLine = true;
|
|
551
|
+
}
|
|
552
|
+
if (!wroteLine)
|
|
553
|
+
console.log(`${name}\tnone\t-`);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
482
556
|
function readPackageVersion() {
|
|
483
557
|
try {
|
|
484
558
|
const packageJson = JSON.parse(readFileSync(join(packageRoot, "package.json"), "utf8"));
|
package/dist/src/project.js
CHANGED
|
@@ -3,6 +3,7 @@ import { basename, dirname, join, resolve } from "node:path";
|
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
import YAML from "yaml";
|
|
5
5
|
import { DEFAULT_AGENT, initializeCapabilities, installSkills } from "./capabilities.js";
|
|
6
|
+
import { assertKnownAgentTarget } from "./agents.js";
|
|
6
7
|
import { copyDirectory, exists, isNonEmptyDirectory, movePath, readJson, writeJson } from "./files.js";
|
|
7
8
|
import { packageify, slugify, titleFromSlug } from "./names.js";
|
|
8
9
|
import { AGENT_STACK } from "./stack.js";
|
|
@@ -90,7 +91,7 @@ export async function createProject(options) {
|
|
|
90
91
|
const slug = slugify(options.slug ?? title);
|
|
91
92
|
const packageName = packageify(options.packageName ?? slug);
|
|
92
93
|
const preset = options.preset ?? "default";
|
|
93
|
-
const agent = options.agent ?? DEFAULT_AGENT;
|
|
94
|
+
const agent = assertKnownAgentTarget(options.agent ?? DEFAULT_AGENT);
|
|
94
95
|
if (!AGENT_STACK.presets[preset]) {
|
|
95
96
|
throw new Error(`unknown capability preset: ${preset}. Expected one of: ${Object.keys(AGENT_STACK.presets).join(", ")}`);
|
|
96
97
|
}
|
|
@@ -135,6 +136,7 @@ export async function doctorProject(root) {
|
|
|
135
136
|
"configs/agent-stack.yaml",
|
|
136
137
|
"configs/capabilities.yaml",
|
|
137
138
|
"docs/agent/capability-profile.md",
|
|
139
|
+
"docs/agent/mcp-setup.md",
|
|
138
140
|
"docs/agent/generated",
|
|
139
141
|
"sources/source-ledger.csv",
|
|
140
142
|
"sota/literature-matrix.csv",
|
|
@@ -205,7 +207,7 @@ async function writeGeneratedPackageJson(root, { slug }) {
|
|
|
205
207
|
const path = join(root, "package.json");
|
|
206
208
|
const data = await readJson(path);
|
|
207
209
|
const existingSpec = data.devDependencies?.["create-academic-research"];
|
|
208
|
-
const packageSpec = process.env.CREATE_ACADEMIC_RESEARCH_PACKAGE_SPEC ?? existingSpec ?? "0.1.
|
|
210
|
+
const packageSpec = process.env.CREATE_ACADEMIC_RESEARCH_PACKAGE_SPEC ?? existingSpec ?? "0.1.6";
|
|
209
211
|
data.name = slug;
|
|
210
212
|
data.devDependencies = {
|
|
211
213
|
...(data.devDependencies ?? {}),
|
package/dist/src/prompts.js
CHANGED
|
@@ -9,7 +9,8 @@ export async function askCreateOptions(defaults, locks = {}) {
|
|
|
9
9
|
const preset = await question(rl, "Capability preset", defaults.preset);
|
|
10
10
|
const agent = await question(rl, "Agent target", defaults.agent);
|
|
11
11
|
const installSkills = locks.installSkills ?? (await yesNo(rl, "Install project-local skills now", defaults.installSkills));
|
|
12
|
-
const installMcpTools = locks.installMcpTools ??
|
|
12
|
+
const installMcpTools = locks.installMcpTools ??
|
|
13
|
+
(await yesNo(rl, "Run finite external MCP tool installers now", defaults.installMcpTools));
|
|
13
14
|
return { title, slug, packageName, preset, agent, installSkills, installMcpTools };
|
|
14
15
|
}
|
|
15
16
|
finally {
|
package/dist/src/stack.d.ts
CHANGED
|
@@ -8,13 +8,21 @@ export interface CapabilityPreset {
|
|
|
8
8
|
mcp_servers: string[];
|
|
9
9
|
}
|
|
10
10
|
export interface McpServer {
|
|
11
|
+
readiness: string;
|
|
11
12
|
priority: string;
|
|
13
|
+
execution_mode: string;
|
|
12
14
|
source_need: string;
|
|
15
|
+
source: string;
|
|
16
|
+
hosted_url: string;
|
|
13
17
|
install_command: string;
|
|
14
18
|
uninstall_command: string;
|
|
19
|
+
setup_commands: string[];
|
|
15
20
|
command: string;
|
|
16
21
|
args: string[];
|
|
17
22
|
env: Record<string, string>;
|
|
23
|
+
required_env: string[];
|
|
24
|
+
recommended_env: string[];
|
|
25
|
+
local_service: string;
|
|
18
26
|
smoke_test: string;
|
|
19
27
|
risks: string;
|
|
20
28
|
}
|
package/dist/src/stack.js
CHANGED
|
@@ -20,38 +20,6 @@ export const AGENT_STACK = {
|
|
|
20
20
|
commands: [
|
|
21
21
|
"npm exec --yes --package skills -- skills add existential-birds/beagle {agent_flag} --skill docling --copy -y"
|
|
22
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
23
|
}
|
|
56
24
|
},
|
|
57
25
|
presets: {
|
|
@@ -61,119 +29,205 @@ export const AGENT_STACK = {
|
|
|
61
29
|
mcp_servers: []
|
|
62
30
|
},
|
|
63
31
|
default: {
|
|
64
|
-
description: "Clean academic research setup with
|
|
32
|
+
description: "Clean academic research setup with the low-friction arXiv MCP record.",
|
|
65
33
|
skill_bundles: ["academic_research"],
|
|
66
|
-
mcp_servers: ["arxiv"
|
|
34
|
+
mcp_servers: ["arxiv"]
|
|
67
35
|
},
|
|
68
36
|
enhanced: {
|
|
69
37
|
description: "Default academic setup plus complementary agent engineering, document, frontend, testing, and doc conversion skills.",
|
|
70
38
|
skill_bundles: ["academic_research", "default_complementary", "docling"],
|
|
71
|
-
mcp_servers: ["arxiv"
|
|
39
|
+
mcp_servers: ["arxiv"]
|
|
72
40
|
},
|
|
73
41
|
literature: {
|
|
74
|
-
description: "Literature-heavy SOTA, survey, scoping, or systematic review setup.",
|
|
42
|
+
description: "Literature-heavy SOTA, survey, scoping, or systematic review setup with CS bibliography support.",
|
|
75
43
|
skill_bundles: ["academic_research", "default_complementary", "docling"],
|
|
76
|
-
mcp_servers: ["arxiv", "
|
|
44
|
+
mcp_servers: ["arxiv", "dblp"]
|
|
77
45
|
},
|
|
78
46
|
writing: {
|
|
79
|
-
description: "Paper-writing
|
|
80
|
-
skill_bundles: [
|
|
81
|
-
|
|
82
|
-
"default_complementary",
|
|
83
|
-
"docling",
|
|
84
|
-
"optional_mechanical_specialists"
|
|
85
|
-
],
|
|
86
|
-
mcp_servers: ["arxiv", "semantic-scholar", "crossref", "overleaf"]
|
|
47
|
+
description: "Paper-writing setup with Overleaf documented as an opt-in credentialed integration.",
|
|
48
|
+
skill_bundles: ["academic_research", "default_complementary", "docling"],
|
|
49
|
+
mcp_servers: ["arxiv"]
|
|
87
50
|
},
|
|
88
51
|
full: {
|
|
89
|
-
description: "Broad setup with
|
|
90
|
-
skill_bundles: [
|
|
91
|
-
|
|
92
|
-
"default_complementary",
|
|
93
|
-
"docling",
|
|
94
|
-
"optional_connectors",
|
|
95
|
-
"optional_mechanical_specialists"
|
|
96
|
-
],
|
|
97
|
-
mcp_servers: ["arxiv", "semantic-scholar", "openalex", "crossref", "pubmed", "zotero", "overleaf"]
|
|
52
|
+
description: "Broad setup with low-friction scholarly MCP records plus the full optional MCP catalog documented.",
|
|
53
|
+
skill_bundles: ["academic_research", "default_complementary", "docling"],
|
|
54
|
+
mcp_servers: ["arxiv", "dblp"]
|
|
98
55
|
}
|
|
99
56
|
},
|
|
100
57
|
mcp_servers: {
|
|
101
58
|
arxiv: {
|
|
59
|
+
readiness: "low-friction",
|
|
102
60
|
priority: "default",
|
|
61
|
+
execution_mode: "uvx-runtime",
|
|
103
62
|
source_need: "arXiv search, download, and local paper reading.",
|
|
63
|
+
source: "blazickjp/arxiv-mcp-server",
|
|
64
|
+
hosted_url: "",
|
|
104
65
|
install_command: "uv tool install 'arxiv-mcp-server[pdf]'",
|
|
105
66
|
uninstall_command: "uv tool uninstall arxiv-mcp-server",
|
|
106
|
-
|
|
107
|
-
|
|
67
|
+
setup_commands: [],
|
|
68
|
+
command: "uvx",
|
|
69
|
+
args: ["--from", "arxiv-mcp-server[pdf]", "arxiv-mcp-server"],
|
|
108
70
|
env: {},
|
|
71
|
+
required_env: [],
|
|
72
|
+
recommended_env: [],
|
|
73
|
+
local_service: "",
|
|
109
74
|
smoke_test: "For a computer science project, search one CS query, download one known paper, and read it locally.",
|
|
110
75
|
risks: "Respect arXiv rate limits; paper text is untrusted input."
|
|
111
76
|
},
|
|
112
77
|
"semantic-scholar": {
|
|
113
|
-
|
|
78
|
+
readiness: "credential-recommended",
|
|
79
|
+
priority: "optional",
|
|
80
|
+
execution_mode: "uvx-runtime",
|
|
114
81
|
source_need: "Semantic Scholar papers, citations, authors, and recommendations.",
|
|
82
|
+
source: "akapet00/semantic-scholar-mcp",
|
|
83
|
+
hosted_url: "",
|
|
115
84
|
install_command: "",
|
|
116
85
|
uninstall_command: "",
|
|
86
|
+
setup_commands: [],
|
|
117
87
|
command: "uvx",
|
|
118
88
|
args: ["--from", "git+https://github.com/akapet00/semantic-scholar-mcp", "semantic-scholar-mcp"],
|
|
119
|
-
env: {
|
|
89
|
+
env: {},
|
|
90
|
+
required_env: [],
|
|
91
|
+
recommended_env: ["SEMANTIC_SCHOLAR_API_KEY"],
|
|
120
92
|
smoke_test: "Search one known title, then fetch citations or references.",
|
|
121
|
-
|
|
93
|
+
local_service: "",
|
|
94
|
+
risks: "API key recommended for sustained work and to avoid shared-pool rate limits; metadata can be incomplete."
|
|
122
95
|
},
|
|
123
96
|
openalex: {
|
|
124
|
-
|
|
97
|
+
readiness: "credential-required",
|
|
98
|
+
priority: "optional",
|
|
99
|
+
execution_mode: "npx-runtime",
|
|
125
100
|
source_need: "OpenAlex broad scholarly graph.",
|
|
101
|
+
source: "cyanheads/openalex-mcp-server",
|
|
102
|
+
hosted_url: "https://openalex.caseyjhand.com/mcp",
|
|
126
103
|
install_command: "",
|
|
127
104
|
uninstall_command: "",
|
|
105
|
+
setup_commands: [],
|
|
128
106
|
command: "npx",
|
|
129
|
-
args: ["-y", "@cyanheads/openalex-mcp-server"],
|
|
130
|
-
env: {
|
|
107
|
+
args: ["-y", "@cyanheads/openalex-mcp-server@latest"],
|
|
108
|
+
env: {},
|
|
109
|
+
required_env: ["OPENALEX_API_KEY"],
|
|
110
|
+
recommended_env: [],
|
|
111
|
+
local_service: "",
|
|
131
112
|
smoke_test: "Search works by title or DOI and confirm stable OpenAlex IDs.",
|
|
132
|
-
risks: "
|
|
113
|
+
risks: "The selected local server requires OPENALEX_API_KEY. OpenAlex keys are free for normal academic use with daily free usage; smoke-test coverage and cost headers before high-volume work."
|
|
133
114
|
},
|
|
134
115
|
crossref: {
|
|
116
|
+
readiness: "manual",
|
|
135
117
|
priority: "manual",
|
|
118
|
+
execution_mode: "manual",
|
|
136
119
|
source_need: "DOI and publication metadata.",
|
|
120
|
+
source: "manual selection required; candidate: AiAgentKarl/crossref-academic-mcp-server",
|
|
121
|
+
hosted_url: "",
|
|
137
122
|
install_command: "",
|
|
138
123
|
uninstall_command: "",
|
|
124
|
+
setup_commands: [],
|
|
139
125
|
command: "",
|
|
140
126
|
args: [],
|
|
141
127
|
env: {},
|
|
128
|
+
required_env: [],
|
|
129
|
+
recommended_env: [],
|
|
130
|
+
local_service: "",
|
|
142
131
|
smoke_test: "Resolve one DOI into publication metadata after choosing a maintained local server.",
|
|
143
|
-
risks: "Manual integration only;
|
|
132
|
+
risks: "Manual integration only; current zero-friction Crossref-only MCP candidates are less mature than arXiv, DBLP, PubMed, or OpenAlex."
|
|
144
133
|
},
|
|
145
134
|
pubmed: {
|
|
135
|
+
readiness: "domain-specific",
|
|
146
136
|
priority: "domain-specific",
|
|
137
|
+
execution_mode: "npx-runtime",
|
|
147
138
|
source_need: "PubMed and biomedical literature.",
|
|
139
|
+
source: "cyanheads/pubmed-mcp-server",
|
|
140
|
+
hosted_url: "https://pubmed.caseyjhand.com/mcp",
|
|
148
141
|
install_command: "",
|
|
149
142
|
uninstall_command: "",
|
|
143
|
+
setup_commands: [],
|
|
150
144
|
command: "npx",
|
|
151
|
-
args: ["-y", "@cyanheads/pubmed-mcp-server"],
|
|
152
|
-
env: {
|
|
145
|
+
args: ["-y", "@cyanheads/pubmed-mcp-server@latest"],
|
|
146
|
+
env: { MCP_TRANSPORT_TYPE: "stdio", MCP_LOG_LEVEL: "warning" },
|
|
147
|
+
required_env: [],
|
|
148
|
+
recommended_env: ["NCBI_API_KEY", "NCBI_ADMIN_EMAIL"],
|
|
149
|
+
local_service: "",
|
|
153
150
|
smoke_test: "Search one PMID or title and fetch metadata.",
|
|
154
|
-
risks: "Domain-specific; observe NCBI rate limits."
|
|
151
|
+
risks: "Domain-specific; observe NCBI rate limits. API key and contact email improve reliability."
|
|
152
|
+
},
|
|
153
|
+
dblp: {
|
|
154
|
+
readiness: "low-friction-cs",
|
|
155
|
+
priority: "cs",
|
|
156
|
+
execution_mode: "uvx-runtime",
|
|
157
|
+
source_need: "DBLP computer science bibliography, venues, authors, and BibTeX.",
|
|
158
|
+
source: "szeider/mcp-dblp",
|
|
159
|
+
hosted_url: "",
|
|
160
|
+
install_command: "",
|
|
161
|
+
uninstall_command: "",
|
|
162
|
+
setup_commands: [],
|
|
163
|
+
command: "uvx",
|
|
164
|
+
args: ["mcp-dblp"],
|
|
165
|
+
env: {},
|
|
166
|
+
required_env: [],
|
|
167
|
+
recommended_env: [],
|
|
168
|
+
local_service: "",
|
|
169
|
+
smoke_test: "Search one known CS paper title and export or inspect its DBLP BibTeX.",
|
|
170
|
+
risks: "CS-specific metadata source; use with arXiv/Semantic Scholar/OpenAlex for coverage beyond DBLP."
|
|
155
171
|
},
|
|
156
172
|
zotero: {
|
|
173
|
+
readiness: "local-service",
|
|
157
174
|
priority: "local-library",
|
|
175
|
+
execution_mode: "local-service",
|
|
158
176
|
source_need: "Zotero local library and attachments.",
|
|
177
|
+
source: "eric-tramel/zoty",
|
|
178
|
+
hosted_url: "",
|
|
159
179
|
install_command: "",
|
|
160
180
|
uninstall_command: "",
|
|
181
|
+
setup_commands: ["uvx --refresh zoty setup", "uvx --refresh zoty doctor"],
|
|
161
182
|
command: "uvx",
|
|
162
183
|
args: ["zoty", "mcp"],
|
|
163
184
|
env: {},
|
|
185
|
+
required_env: [],
|
|
186
|
+
recommended_env: [],
|
|
187
|
+
local_service: "Zotero desktop running with local API enabled; Zoty Bridge required for attachment and collection write operations.",
|
|
164
188
|
smoke_test: "List collections, search one known item, and export BibTeX.",
|
|
165
189
|
risks: "Requires Zotero local API and bridge setup."
|
|
166
190
|
},
|
|
167
191
|
overleaf: {
|
|
192
|
+
readiness: "manual-credentialed",
|
|
168
193
|
priority: "writing",
|
|
194
|
+
execution_mode: "manual-local",
|
|
169
195
|
source_need: "Overleaf project sync.",
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
196
|
+
source: "YounesBensafia/overleaf-mcp-server",
|
|
197
|
+
hosted_url: "",
|
|
198
|
+
install_command: "",
|
|
199
|
+
uninstall_command: "",
|
|
200
|
+
setup_commands: [],
|
|
201
|
+
command: "",
|
|
202
|
+
args: [],
|
|
203
|
+
env: {},
|
|
204
|
+
required_env: ["OVERLEAF_TOKEN"],
|
|
205
|
+
recommended_env: ["PROJECT_ID"],
|
|
206
|
+
local_service: "Local clone of the Overleaf MCP server configured with uv and an Overleaf project that supports Git sync.",
|
|
175
207
|
smoke_test: "List projects or read one .tex file; do not write by default.",
|
|
176
|
-
risks: "Requires token setup; write access needs explicit approval."
|
|
208
|
+
risks: "Requires token and project setup; write/push access needs explicit approval."
|
|
209
|
+
},
|
|
210
|
+
"paper-search": {
|
|
211
|
+
readiness: "fallback",
|
|
212
|
+
priority: "fallback",
|
|
213
|
+
execution_mode: "manual-fallback",
|
|
214
|
+
source_need: "Multi-source paper search and download fallback across many scholarly sources.",
|
|
215
|
+
source: "openags/paper-search-mcp",
|
|
216
|
+
hosted_url: "",
|
|
217
|
+
install_command: "",
|
|
218
|
+
uninstall_command: "",
|
|
219
|
+
setup_commands: [],
|
|
220
|
+
command: "",
|
|
221
|
+
args: [],
|
|
222
|
+
env: {},
|
|
223
|
+
required_env: [],
|
|
224
|
+
recommended_env: [
|
|
225
|
+
"PAPER_SEARCH_MCP_UNPAYWALL_EMAIL",
|
|
226
|
+
"PAPER_SEARCH_MCP_SEMANTIC_SCHOLAR_API_KEY"
|
|
227
|
+
],
|
|
228
|
+
local_service: "Manual review required before enabling; configure only permitted sources.",
|
|
229
|
+
smoke_test: "Search one harmless query with a source allow-list and verify provenance for each result.",
|
|
230
|
+
risks: "Powerful aggregator with optional restricted-source workflows; keep Sci-Hub or questionable download features disabled unless explicitly accepted."
|
|
177
231
|
}
|
|
178
232
|
}
|
|
179
233
|
};
|
package/package.json
CHANGED
package/template/README.md
CHANGED
|
@@ -53,23 +53,30 @@ Project-local skills and MCP records are managed with:
|
|
|
53
53
|
|
|
54
54
|
```bash
|
|
55
55
|
npx academic-research skills presets
|
|
56
|
+
npx academic-research agents list
|
|
56
57
|
npx academic-research skills install --preset default
|
|
57
58
|
npx academic-research skills install --preset enhanced
|
|
58
59
|
npx academic-research skills list
|
|
59
60
|
npx academic-research skills status
|
|
60
|
-
npx academic-research mcp enable arxiv semantic-scholar openalex
|
|
61
61
|
npx academic-research mcp list
|
|
62
|
+
npx academic-research mcp env openalex semantic-scholar zotero
|
|
63
|
+
npx academic-research mcp enable arxiv dblp
|
|
62
64
|
npx academic-research mcp commands arxiv
|
|
63
65
|
npx academic-research mcp install arxiv
|
|
66
|
+
npx academic-research mcp doctor
|
|
64
67
|
```
|
|
65
68
|
|
|
66
69
|
`skills list` reports installed project-local skills. `skills presets` reports
|
|
67
70
|
available install presets. `mcp enable` changes project records. `mcp commands`
|
|
68
|
-
prints finite external install commands without running them. `mcp
|
|
69
|
-
|
|
70
|
-
|
|
71
|
+
prints finite external install commands without running them. `mcp env` prints
|
|
72
|
+
env vars, hosted endpoints, local prerequisites, and setup commands before you
|
|
73
|
+
enable optional servers. `mcp install` runs only finite tool installation
|
|
74
|
+
commands; runtime-only `uvx`/`npx` MCP servers may have no install step and are
|
|
75
|
+
started later by the MCP client.
|
|
71
76
|
|
|
72
77
|
`default` installs the companion academic research skill package and keeps the
|
|
73
|
-
MCP records focused on
|
|
74
|
-
|
|
75
|
-
|
|
78
|
+
MCP records focused on low-friction arXiv discovery. `literature` and `full`
|
|
79
|
+
add DBLP for computer science bibliography. Credentialed, local-service, or
|
|
80
|
+
domain-specific MCP servers such as OpenAlex, Semantic Scholar, PubMed, Zotero,
|
|
81
|
+
and Overleaf should be enabled only after reading `docs/agent/mcp-setup.md` and
|
|
82
|
+
checking their prerequisites with `mcp env`.
|
package/template/package.json
CHANGED
|
@@ -9,9 +9,10 @@
|
|
|
9
9
|
"skills:presets": "academic-research skills presets",
|
|
10
10
|
"mcp:list": "academic-research mcp list",
|
|
11
11
|
"mcp:commands": "academic-research mcp commands",
|
|
12
|
+
"mcp:env": "academic-research mcp env",
|
|
12
13
|
"mcp:doctor": "academic-research mcp doctor"
|
|
13
14
|
},
|
|
14
15
|
"devDependencies": {
|
|
15
|
-
"create-academic-research": "0.1.
|
|
16
|
+
"create-academic-research": "0.1.6"
|
|
16
17
|
}
|
|
17
18
|
}
|