create-academic-research 0.1.4 → 0.1.5

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
@@ -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 <name>` only when you want to
39
- force a specific target recognized by the `skills` CLI, such as `claude-code`,
40
- `codex`, `cursor`, `windsurf`, or another supported local loader.
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
 
@@ -76,6 +77,7 @@ Inside a generated project:
76
77
  ```bash
77
78
  npx academic-research doctor
78
79
  npx academic-research rename --title "New Title" --slug new-title --package new_title
80
+ npx academic-research agents list
79
81
  npx academic-research skills presets
80
82
  npx academic-research skills install --preset default
81
83
  npx academic-research skills install --preset enhanced
@@ -134,8 +136,9 @@ The create wizard can install that project-local package automatically.
134
136
  Those skills are portable `SKILL.md` instructions, but they require an
135
137
  agent/runtime that can load skills or include the relevant instructions in
136
138
  context. They are not automatic capabilities of every raw model API.
137
- Use `--agent <agent>` for explicit setup, for example `--agent codex` or
138
- `--agent claude-code`.
139
+ Use `--agent <id>` for explicit setup with any id from
140
+ `academic-research agents list`. The shorthand `--agent claude` is normalized
141
+ to the supported `claude-code` target.
139
142
  Avoid `--agent auto` for unattended setup: the upstream `skills` CLI may expand
140
143
  it to every agent it detects on the machine.
141
144
 
@@ -166,8 +169,8 @@ Releases are tag-driven. Update `package.json` and `package-lock.json`, commit
166
169
  the change, create `vX.Y.Z`, and push the tag:
167
170
 
168
171
  ```bash
169
- git tag -a v0.1.4 -m "v0.1.4"
170
- git push origin main v0.1.4
172
+ git tag -a v0.1.5 -m "v0.1.5"
173
+ git push origin main v0.1.5
171
174
  ```
172
175
 
173
176
  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 declare const DEFAULT_AGENT = "universal";
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 {};
@@ -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 const DEFAULT_AGENT = "universal";
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,7 +18,7 @@ export async function readCapabilities(root) {
18
18
  }
19
19
  export async function writeCapabilities(root, state) {
20
20
  const next = {
21
- agent: normalizeAgent(state.agent),
21
+ agent: assertKnownAgentTarget(state.agent),
22
22
  preset: state.preset ?? "default",
23
23
  scope: "project-local",
24
24
  mcp_servers: [...(state.mcp_servers ?? [])]
@@ -42,7 +42,7 @@ export async function buildSkillInstallCommands(root, preset = "default", option
42
42
  if (!selected)
43
43
  throw new Error(`unknown skill preset: ${preset}`);
44
44
  const state = await readCapabilities(root);
45
- const agent = normalizeAgent(options.agent ?? state.agent);
45
+ const agent = assertKnownAgentTarget(options.agent ?? state.agent);
46
46
  const commands = [];
47
47
  for (const bundleName of selected.skill_bundles) {
48
48
  const bundle = AGENT_STACK.skill_bundles[bundleName];
@@ -61,7 +61,7 @@ export async function buildSkillInstallCommands(root, preset = "default", option
61
61
  }
62
62
  export async function installSkills(root, preset = "default", options = {}, runner = defaultRunner) {
63
63
  const state = await readCapabilities(root);
64
- const agent = normalizeAgent(options.agent ?? state.agent);
64
+ const agent = assertKnownAgentTarget(options.agent ?? state.agent);
65
65
  const commands = await buildSkillInstallCommands(root, preset, options);
66
66
  for (const command of commands) {
67
67
  await runner.run(command, { cwd: root });
@@ -135,7 +135,7 @@ export async function enableMcpServers(root, servers, options = {}) {
135
135
  const selected = dedupe([...(state.mcp_servers ?? []), ...servers]);
136
136
  await writeCapabilities(root, {
137
137
  ...state,
138
- agent: options.agent ?? state.agent,
138
+ agent: assertKnownAgentTarget(options.agent ?? state.agent),
139
139
  mcp_servers: selected
140
140
  });
141
141
  return { ok: true, servers: selected };
@@ -147,7 +147,7 @@ export async function disableMcpServers(root, servers, options = {}) {
147
147
  const selected = (state.mcp_servers ?? []).filter((server) => !blocked.has(server));
148
148
  await writeCapabilities(root, {
149
149
  ...state,
150
- agent: options.agent ?? state.agent,
150
+ agent: assertKnownAgentTarget(options.agent ?? state.agent),
151
151
  mcp_servers: selected
152
152
  });
153
153
  return { ok: true, servers: selected };
@@ -269,7 +269,7 @@ async function writeCapabilityProfile(root, state) {
269
269
  const lines = [
270
270
  "# Agent Capability Profile",
271
271
  "",
272
- `- Agent target: \`${normalizeAgent(state.agent)}\``,
272
+ `- Agent target: \`${assertKnownAgentTarget(state.agent)}\``,
273
273
  `- Preset: \`${state.preset ?? "default"}\``,
274
274
  "- Scope: `project-local`",
275
275
  "",
@@ -307,7 +307,7 @@ function dedupe(values) {
307
307
  return [...new Set(values)];
308
308
  }
309
309
  function renderSkillCommand(command, agent) {
310
- const normalized = normalizeAgent(agent);
310
+ const normalized = assertKnownAgentTarget(agent);
311
311
  const agentFlag = normalized === AUTO_AGENT ? "" : `--agent '${normalized}'`;
312
312
  return command.replaceAll("{agent_flag}", agentFlag).replaceAll("{agent}", normalized);
313
313
  }
@@ -352,7 +352,7 @@ function splitCommand(command) {
352
352
  function normalizeCapabilityState(value) {
353
353
  const record = typeof value === "object" && value !== null ? value : {};
354
354
  return {
355
- agent: normalizeAgent(typeof record.agent === "string" ? record.agent : undefined),
355
+ agent: assertKnownAgentTarget(typeof record.agent === "string" ? record.agent : undefined),
356
356
  preset: typeof record.preset === "string" ? record.preset : "default",
357
357
  scope: "project-local",
358
358
  mcp_servers: Array.isArray(record.mcp_servers)
@@ -397,12 +397,8 @@ async function removeSkillsFromLock(root, skills) {
397
397
  await writeFile(path, `${JSON.stringify(record, null, 2)}\n`, "utf8");
398
398
  }
399
399
  }
400
- function normalizeAgent(agent) {
401
- const value = agent?.trim();
402
- return value ? value : DEFAULT_AGENT;
403
- }
404
400
  function mcpSnippetFileName(agent) {
405
- const normalized = normalizeAgent(agent);
401
+ const normalized = normalizeAgentTarget(agent);
406
402
  return normalized === DEFAULT_AGENT || normalized === AUTO_AGENT ? "mcp.json" : `${normalized}-mcp.json`;
407
403
  }
408
404
  async function removeInactiveMcpSnippets(outputDir, activeFile) {
package/dist/src/cli.js CHANGED
@@ -5,6 +5,7 @@ import { disableMcpServers, doctorMcpServers, enableMcpServers, DEFAULT_AGENT, i
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);
@@ -401,9 +419,15 @@ export function formatInteractiveCreateGuide() {
401
419
  ...presetLines,
402
420
  "",
403
421
  "Agent target:",
404
- " universal Default. Installs one shared project-local .agents/skills copy.",
405
- " codex Codex-specific project-local skill install.",
406
- " auto Lets the upstream skills CLI detect agents; may install to multiple agent loaders.",
422
+ " universal Recommended. One shared project-local .agents/skills copy.",
423
+ " auto Let the skills CLI detect installed agents; may create multiple agent-specific copies.",
424
+ " <id> Any supported skills.sh agent id.",
425
+ "",
426
+ "Supported specific agent ids:",
427
+ ...formatSupportedAgentTargetLines(),
428
+ "",
429
+ "Aliases:",
430
+ ...formatAgentAliasLines(),
407
431
  "",
408
432
  "Skill and MCP behavior:",
409
433
  " Skills are copied into the project, not installed globally.",
@@ -426,7 +450,7 @@ function printCreateHelp() {
426
450
  " --package <name> Python package name. Default: normalized project name.",
427
451
  " --preset <name> Capability preset: minimal, default, enhanced, literature, writing, full.",
428
452
  " --profile <name> Project profile metadata. Default: academic-general.",
429
- " --agent <name> Agent target. Default: universal.",
453
+ " --agent <id> Agent target: universal, auto, or a supported skills.sh id.",
430
454
  " --install-skills Install project-local skills without prompting.",
431
455
  " --no-install-skills Skip project-local skill installation.",
432
456
  " --install-mcp-tools Run finite external MCP install commands after creation.",
@@ -445,7 +469,7 @@ function printMissingTargetHelp() {
445
469
  }
446
470
  function printLifecycleHelp() {
447
471
  console.log([
448
- "Usage: academic-research <doctor|rename|skills|mcp>",
472
+ "Usage: academic-research <doctor|rename|agents|skills|mcp>",
449
473
  "",
450
474
  "Manage a generated academic research repository after creation.",
451
475
  "",
@@ -454,6 +478,21 @@ function printLifecycleHelp() {
454
478
  " -v, --version Show package version."
455
479
  ].join("\n"));
456
480
  }
481
+ function printAgentsHelp() {
482
+ console.log([
483
+ "Usage: academic-research agents <list>",
484
+ "",
485
+ "List supported project-local agent targets.",
486
+ "",
487
+ "Targets:",
488
+ " universal Recommended shared project-local .agents/skills copy.",
489
+ " auto Let the skills CLI detect installed agents.",
490
+ " <id> A supported skills.sh agent id.",
491
+ "",
492
+ "Options:",
493
+ " -h, --help Show this help."
494
+ ].join("\n"));
495
+ }
457
496
  function printSkillsHelp() {
458
497
  console.log([
459
498
  "Usage: academic-research skills <list|status|presets|install|remove|uninstall|update> [options]",
@@ -463,7 +502,7 @@ function printSkillsHelp() {
463
502
  "Options:",
464
503
  " --root <path> Project root for list, status, install, remove, uninstall, update.",
465
504
  " --preset <name> Capability preset for install.",
466
- " --agent <name> Agent selector for install. Default: project capability agent.",
505
+ " --agent <id> Agent selector for install. Default: project capability agent.",
467
506
  " -h, --help Show this help."
468
507
  ].join("\n"));
469
508
  }
@@ -475,7 +514,7 @@ function printMcpHelp() {
475
514
  "",
476
515
  "Options:",
477
516
  " --root <path> Project root for project-state commands.",
478
- " --agent <name> Agent for enable/disable generated snippets.",
517
+ " --agent <id> Agent for enable/disable generated snippets.",
479
518
  " -h, --help Show this help."
480
519
  ].join("\n"));
481
520
  }
@@ -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
  }
@@ -205,7 +206,7 @@ async function writeGeneratedPackageJson(root, { slug }) {
205
206
  const path = join(root, "package.json");
206
207
  const data = await readJson(path);
207
208
  const existingSpec = data.devDependencies?.["create-academic-research"];
208
- const packageSpec = process.env.CREATE_ACADEMIC_RESEARCH_PACKAGE_SPEC ?? existingSpec ?? "0.1.4";
209
+ const packageSpec = process.env.CREATE_ACADEMIC_RESEARCH_PACKAGE_SPEC ?? existingSpec ?? "0.1.5";
209
210
  data.name = slug;
210
211
  data.devDependencies = {
211
212
  ...(data.devDependencies ?? {}),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-academic-research",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "Create and manage agent-ready academic research repositories.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -53,6 +53,7 @@ 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
@@ -12,6 +12,6 @@
12
12
  "mcp:doctor": "academic-research mcp doctor"
13
13
  },
14
14
  "devDependencies": {
15
- "create-academic-research": "0.1.4"
15
+ "create-academic-research": "0.1.5"
16
16
  }
17
17
  }