create-academic-research 0.1.7 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -93,6 +93,7 @@ npx academic-research agents list
93
93
  npx academic-research skills presets
94
94
  npx academic-research skills install --preset default
95
95
  npx academic-research skills install --preset enhanced
96
+ npx academic-research skills install source-ingestion sota-literature-review
96
97
  npx academic-research skills list
97
98
  npx academic-research skills status
98
99
  npx academic-research skills remove source-ingestion
@@ -122,7 +123,7 @@ Skills are project-local by default.
122
123
  | Command | Meaning |
123
124
  |---|---|
124
125
  | `skills presets` | List available capability presets. |
125
- | `skills install` | Install project-local skills for the selected preset and project agent. This does not change MCP records. |
126
+ | `skills install` | Install project-local skills by preset, or selected skill ids such as `source-ingestion`. This does not change MCP records. |
126
127
  | `skills list` | List skills found in project-local skill loader directories. |
127
128
  | `skills status` | Show configured project preset, agent, scope, skill roots, unique skill ids, and installed copies. |
128
129
  | `skills remove` / `skills uninstall` | Remove selected project-local skills. |
@@ -215,8 +216,8 @@ Releases are tag-driven. Update `package.json` and `package-lock.json`, commit
215
216
  the change, create `vX.Y.Z`, and push the tag:
216
217
 
217
218
  ```bash
218
- git tag -a v0.1.7 -m "v0.1.7"
219
- git push origin main v0.1.7
219
+ git tag -a v0.1.8 -m "v0.1.8"
220
+ git push origin main v0.1.8
220
221
  ```
221
222
 
222
223
  Once the GitHub repository is public, the release workflow validates the tag
@@ -16,6 +16,7 @@ export interface InitializeCapabilitiesOptions {
16
16
  export interface CapabilityCommandResult {
17
17
  ok: true;
18
18
  count?: number;
19
+ skills?: string[];
19
20
  servers?: string[];
20
21
  }
21
22
  export interface InstalledSkill {
@@ -36,7 +37,9 @@ export declare function readCapabilities(root: string): Promise<CapabilityState>
36
37
  export declare function writeCapabilities(root: string, state: Partial<CapabilityState>): Promise<void>;
37
38
  export declare function initializeCapabilities(root: string, options?: InitializeCapabilitiesOptions): Promise<void>;
38
39
  export declare function buildSkillInstallCommands(root: string, preset?: string, options?: SkillInstallOptions): Promise<string[][]>;
40
+ export declare function buildExplicitSkillInstallCommands(root: string, skills: string[], options?: SkillInstallOptions): Promise<string[][]>;
39
41
  export declare function installSkills(root: string, preset?: string, options?: SkillInstallOptions, runner?: Runner): Promise<CapabilityCommandResult>;
42
+ export declare function installSkillIds(root: string, skills: string[], options?: SkillInstallOptions, runner?: Runner): Promise<CapabilityCommandResult>;
40
43
  export declare function listInstalledSkills(root: string): Promise<InstalledSkill[]>;
41
44
  export declare function removeSkills(root: string, skills: string[], runner?: Runner): Promise<CapabilityCommandResult>;
42
45
  export declare function updateSkills(root: string, runner?: Runner): Promise<CapabilityCommandResult>;
@@ -60,6 +60,21 @@ export async function buildSkillInstallCommands(root, preset = "default", option
60
60
  }
61
61
  return commands;
62
62
  }
63
+ export async function buildExplicitSkillInstallCommands(root, skills, options = {}) {
64
+ const selectedSkills = normalizeSkillIds(skills);
65
+ const state = await readCapabilities(root);
66
+ const agent = assertKnownAgentTarget(options.agent ?? state.agent);
67
+ const skillsBySource = new Map();
68
+ for (const skill of selectedSkills) {
69
+ const source = skillSourceForId(skill);
70
+ if (!source)
71
+ throw new Error(`unknown skill id: ${skill}`);
72
+ const sourceSkills = skillsBySource.get(source) ?? [];
73
+ sourceSkills.push(skill);
74
+ skillsBySource.set(source, sourceSkills);
75
+ }
76
+ return [...skillsBySource.entries()].map(([source, sourceSkills]) => skillAddCommand(source, sourceSkills, agent));
77
+ }
63
78
  export async function installSkills(root, preset = "default", options = {}, runner = defaultRunner) {
64
79
  const state = await readCapabilities(root);
65
80
  const agent = assertKnownAgentTarget(options.agent ?? state.agent);
@@ -76,6 +91,22 @@ export async function installSkills(root, preset = "default", options = {}, runn
76
91
  }
77
92
  return { ok: true, count: commands.length };
78
93
  }
94
+ export async function installSkillIds(root, skills, options = {}, runner = defaultRunner) {
95
+ const state = await readCapabilities(root);
96
+ const agent = assertKnownAgentTarget(options.agent ?? state.agent);
97
+ const selectedSkills = normalizeSkillIds(skills);
98
+ const commands = await buildExplicitSkillInstallCommands(root, selectedSkills, options);
99
+ for (const command of commands) {
100
+ await runner.run(command, { cwd: root });
101
+ }
102
+ if (state.agent !== agent) {
103
+ await writeCapabilities(root, {
104
+ ...state,
105
+ agent
106
+ });
107
+ }
108
+ return { ok: true, count: commands.length, skills: selectedSkills };
109
+ }
79
110
  export async function listInstalledSkills(root) {
80
111
  const roots = await discoverProjectSkillRoots(root);
81
112
  const skills = [];
@@ -287,6 +318,7 @@ async function writeCapabilityProfile(root, state) {
287
318
  "## Skills",
288
319
  "",
289
320
  `- Install with: \`academic-research skills install --preset ${state.preset ?? "default"}\``,
321
+ "- Install selected skills with: `academic-research skills install <skill-id> [...]`",
290
322
  "- List installed with: `academic-research skills list`",
291
323
  "- List presets with: `academic-research skills presets`",
292
324
  "- Remove with: `academic-research skills remove <skill>`",
@@ -355,6 +387,48 @@ function renderSkillCommand(command, agent) {
355
387
  const agentFlag = normalized === AUTO_AGENT ? "" : `--agent '${normalized}'`;
356
388
  return command.replaceAll("{agent_flag}", agentFlag).replaceAll("{agent}", normalized);
357
389
  }
390
+ function skillAddCommand(source, skills, agent) {
391
+ return [
392
+ "npm",
393
+ "exec",
394
+ "--yes",
395
+ "--package",
396
+ "skills",
397
+ "--",
398
+ "skills",
399
+ "add",
400
+ source,
401
+ ...skillAgentArgs(agent),
402
+ "--skill",
403
+ ...skills,
404
+ "--copy",
405
+ "-y"
406
+ ];
407
+ }
408
+ function skillAgentArgs(agent) {
409
+ const normalized = assertKnownAgentTarget(agent);
410
+ return normalized === AUTO_AGENT ? [] : ["--agent", normalized];
411
+ }
412
+ function normalizeSkillIds(skills) {
413
+ if (skills.length === 0)
414
+ throw new Error("no skills selected");
415
+ const result = [];
416
+ for (const skill of skills) {
417
+ if (!/^[a-z0-9][a-z0-9._-]*$/.test(skill)) {
418
+ throw new Error(`invalid skill id: ${skill}`);
419
+ }
420
+ if (!result.includes(skill))
421
+ result.push(skill);
422
+ }
423
+ return result;
424
+ }
425
+ function skillSourceForId(skill) {
426
+ for (const source of Object.values(AGENT_STACK.skill_sources)) {
427
+ if (source.skills.includes(skill))
428
+ return source.source;
429
+ }
430
+ return undefined;
431
+ }
358
432
  function appendMcpPrerequisiteLines(lines, requiredEnv, recommendedEnv, localService) {
359
433
  if (requiredEnv.length > 0)
360
434
  lines.push(` - Requires env: ${requiredEnv.map((name) => `\`${name}\``).join(", ")}`);
package/dist/src/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { existsSync, readFileSync } from "node:fs";
2
2
  import { basename, delimiter, dirname, join, resolve } from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
- import { assertKnownMcpServers, 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, installSkillIds, 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";
@@ -9,7 +9,15 @@ import { formatAgentAliasLines, formatAgentTargetList, formatSupportedAgentTarge
9
9
  import { packageify, slugify, titleFromSlug } from "./names.js";
10
10
  const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "../..");
11
11
  const packageVersion = readPackageVersion();
12
- const CREATE_FLAGS = flagSchema(["yes", "help", "version", "install-skills", "no-install-skills", "install-mcp-tools"], ["title", "slug", "package", "preset", "profile", "agent"]);
12
+ const CREATE_FLAGS = flagSchema([
13
+ "yes",
14
+ "help",
15
+ "version",
16
+ "install-skills",
17
+ "no-install-skills",
18
+ "install-mcp-tools",
19
+ "no-install-mcp-tools"
20
+ ], ["title", "slug", "package", "preset", "profile", "agent"]);
13
21
  const ROOT_FLAGS = flagSchema(["help"], ["root"]);
14
22
  const RENAME_FLAGS = flagSchema(["help"], ["root", "title", "slug", "package"]);
15
23
  const SKILLS_FLAGS = flagSchema(["help"], ["root", "preset", "agent"]);
@@ -43,6 +51,9 @@ async function createMain(argv) {
43
51
  if (flagBool(parsed.flags, "install-skills") && flagBool(parsed.flags, "no-install-skills")) {
44
52
  throw new Error("cannot use --install-skills and --no-install-skills together");
45
53
  }
54
+ if (flagBool(parsed.flags, "install-mcp-tools") && flagBool(parsed.flags, "no-install-mcp-tools")) {
55
+ throw new Error("cannot use --install-mcp-tools and --no-install-mcp-tools together");
56
+ }
46
57
  if (parsed.positionals.length > 1) {
47
58
  throw new Error(`unexpected argument: ${parsed.positionals[1]}`);
48
59
  }
@@ -60,7 +71,9 @@ async function createMain(argv) {
60
71
  const installSkillsLock = flagBool(parsed.flags, "install-skills") || flagBool(parsed.flags, "no-install-skills")
61
72
  ? defaults.installSkills
62
73
  : undefined;
63
- const installMcpToolsLock = flagBool(parsed.flags, "install-mcp-tools") ? true : undefined;
74
+ const installMcpToolsLock = flagBool(parsed.flags, "install-mcp-tools") || flagBool(parsed.flags, "no-install-mcp-tools")
75
+ ? defaults.installMcpTools
76
+ : undefined;
64
77
  const answers = interactive
65
78
  ? await askInteractiveCreateOptions(defaults, {
66
79
  installSkills: installSkillsLock,
@@ -240,8 +253,18 @@ async function skillsCommand(argv) {
240
253
  if (subcommand === "install") {
241
254
  assertOnlyOptions(parsed.flags, "skills install", ["root", "preset", "agent"]);
242
255
  const root = resolve(flagString(parsed.flags, "root") ?? ".");
243
- assertNoArguments(parsed.positionals, "skills install");
244
- const preset = flagString(parsed.flags, "preset") ?? "default";
256
+ const explicitSkills = parsed.positionals;
257
+ const explicitPreset = flagString(parsed.flags, "preset");
258
+ if (explicitSkills.length > 0) {
259
+ if (explicitPreset)
260
+ throw new Error("skills install does not accept --preset when skill ids are provided");
261
+ const result = await installSkillIds(root, explicitSkills, {
262
+ agent: flagString(parsed.flags, "agent")
263
+ });
264
+ console.log(`Installed ${result.skills?.length ?? explicitSkills.length} skill(s) with ${result.count ?? 0} command(s).`);
265
+ return 0;
266
+ }
267
+ const preset = explicitPreset ?? "default";
245
268
  const result = await installSkills(root, preset, {
246
269
  agent: flagString(parsed.flags, "agent")
247
270
  });
@@ -516,6 +539,7 @@ function printCreateHelp() {
516
539
  " --install-skills Install project-local skills without prompting.",
517
540
  " --no-install-skills Skip project-local skill installation.",
518
541
  " --install-mcp-tools Run finite external MCP install commands after creation.",
542
+ " --no-install-mcp-tools Skip finite external MCP install commands.",
519
543
  " -h, --help Show this help.",
520
544
  " -v, --version Show package version."
521
545
  ].join("\n"));
@@ -568,13 +592,17 @@ function printAgentsHelp() {
568
592
  }
569
593
  function printSkillsHelp() {
570
594
  console.log([
571
- "Usage: academic-research skills <list|status|presets|install|remove|uninstall|update> [options]",
595
+ "Usage: academic-research skills <list|status|presets|install|remove|uninstall|update> [skill-id...] [options]",
572
596
  "",
573
597
  "Manage project-local skill installs for a generated research repository.",
574
598
  "",
599
+ "Examples:",
600
+ " academic-research skills install --preset default",
601
+ " academic-research skills install source-ingestion sota-literature-review",
602
+ "",
575
603
  "Options:",
576
604
  " --root <path> Project root for list, status, install, remove, uninstall, update.",
577
- " --preset <name> Capability preset for install.",
605
+ " --preset <name> Capability preset for install when no skill ids are provided.",
578
606
  " --agent <id> Agent selector for install. Default: project capability agent.",
579
607
  " -h, --help Show this help."
580
608
  ].join("\n"));
@@ -214,7 +214,7 @@ async function writeGeneratedPackageJson(root, { slug }) {
214
214
  const path = join(root, "package.json");
215
215
  const data = await readJson(path);
216
216
  const existingSpec = data.devDependencies?.["create-academic-research"];
217
- const packageSpec = process.env.CREATE_ACADEMIC_RESEARCH_PACKAGE_SPEC ?? existingSpec ?? "0.1.7";
217
+ const packageSpec = process.env.CREATE_ACADEMIC_RESEARCH_PACKAGE_SPEC ?? existingSpec ?? "0.1.8";
218
218
  data.name = slug;
219
219
  data.devDependencies = {
220
220
  ...(data.devDependencies ?? {}),
@@ -2,6 +2,11 @@ export interface SkillBundle {
2
2
  description: string;
3
3
  commands: string[];
4
4
  }
5
+ export interface SkillSource {
6
+ description: string;
7
+ source: string;
8
+ skills: string[];
9
+ }
5
10
  export interface CapabilityPreset {
6
11
  description: string;
7
12
  skill_bundles: string[];
@@ -30,6 +35,7 @@ export interface AgentStack {
30
35
  version: number;
31
36
  description: string;
32
37
  skill_bundles: Record<string, SkillBundle>;
38
+ skill_sources: Record<string, SkillSource>;
33
39
  presets: Record<string, CapabilityPreset>;
34
40
  mcp_servers: Record<string, McpServer>;
35
41
  }
package/dist/src/stack.js CHANGED
@@ -22,6 +22,76 @@ export const AGENT_STACK = {
22
22
  ]
23
23
  }
24
24
  },
25
+ skill_sources: {
26
+ academic_research: {
27
+ description: "Academic research skills maintained by this project.",
28
+ source: "VincenzoImp/academic-research-skills",
29
+ skills: [
30
+ "academic-mcp-tooling",
31
+ "adversarial-peer-review",
32
+ "artifact-open-science",
33
+ "citation-bibliography-tooling",
34
+ "citation-claim-audit",
35
+ "cs-methodology-evaluation",
36
+ "cs-venue-strategy",
37
+ "document-conversion",
38
+ "ethics-data-governance",
39
+ "experiment-logbook",
40
+ "paper-writing-review",
41
+ "rebuttal-revision-strategy",
42
+ "repo-migration",
43
+ "research-data-analysis",
44
+ "research-design-positioning",
45
+ "research-project-maintenance",
46
+ "research-project-router",
47
+ "research-repo-reproduction",
48
+ "research-ui-prototyping",
49
+ "skill-evaluation",
50
+ "sota-literature-review",
51
+ "source-ingestion",
52
+ "systematic-review-prisma"
53
+ ]
54
+ },
55
+ superpowers: {
56
+ description: "General agent engineering skills from Superpowers.",
57
+ source: "obra/superpowers",
58
+ skills: [
59
+ "brainstorming",
60
+ "dispatching-parallel-agents",
61
+ "executing-plans",
62
+ "finishing-a-development-branch",
63
+ "receiving-code-review",
64
+ "requesting-code-review",
65
+ "subagent-driven-development",
66
+ "systematic-debugging",
67
+ "test-driven-development",
68
+ "using-git-worktrees",
69
+ "using-superpowers",
70
+ "verification-before-completion",
71
+ "writing-plans",
72
+ "writing-skills"
73
+ ]
74
+ },
75
+ anthropics: {
76
+ description: "Document, frontend, testing, MCP, and skill-authoring helpers.",
77
+ source: "anthropics/skills",
78
+ skills: [
79
+ "docx",
80
+ "frontend-design",
81
+ "mcp-builder",
82
+ "pdf",
83
+ "pptx",
84
+ "skill-creator",
85
+ "webapp-testing",
86
+ "xlsx"
87
+ ]
88
+ },
89
+ docling: {
90
+ description: "Document conversion helper from the Beagle collection.",
91
+ source: "existential-birds/beagle",
92
+ skills: ["docling"]
93
+ }
94
+ },
25
95
  presets: {
26
96
  minimal: {
27
97
  description: "Academic research skills only, no MCP records.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-academic-research",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "description": "Create and manage agent-ready academic research repositories.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -57,6 +57,7 @@ npx academic-research skills presets
57
57
  npx academic-research agents list
58
58
  npx academic-research skills install --preset default
59
59
  npx academic-research skills install --preset enhanced
60
+ npx academic-research skills install source-ingestion sota-literature-review
60
61
  npx academic-research skills list
61
62
  npx academic-research skills status
62
63
  npx academic-research setup
@@ -15,6 +15,6 @@
15
15
  "mcp:doctor": "academic-research mcp doctor"
16
16
  },
17
17
  "devDependencies": {
18
- "create-academic-research": "0.1.7"
18
+ "create-academic-research": "0.1.8"
19
19
  }
20
20
  }