wormajs 0.2.7 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -15,10 +15,14 @@ const template_1 = require("../template");
15
15
  * Parse a single line from .wormarc file
16
16
  *
17
17
  * Supported formats:
18
- * - `https://xxxx.com/openapi.json` -> generates in src/api, default alova template
19
- * - `https://yyyy.com/openapi.json, axios` -> generates in src/api2, axios template
20
- * - `myApi=https://zzzz.com/openapi.json` -> generates in src/myApi, default alova template
21
- * - `myApi=https://zzzz.com/openapi.json, fetch` -> generates in src/myApi, fetch template
18
+ * - `https://xxxx.com/openapi.json` -> generates in src/api, default alova template, no aiDoc plugin
19
+ * - `https://yyyy.com/openapi.json, axios` -> generates in src/api2, axios template, no aiDoc plugin
20
+ * - `myApi=https://zzzz.com/openapi.json, fetch` -> generates in src/myApi, fetch template, no aiDoc plugin
21
+ * - `https://xxxx.com/openapi.json, alova, cursor` -> alova template, aiDoc installed to `cursor`
22
+ * - `myApi=https://zzzz.com/openapi.json, fetch, cursor, claude-code` -> fetch template, aiDoc installed to both agents
23
+ *
24
+ * The optional third comma-separated segment is the `agent` list for the `aiDoc`
25
+ * plugin. When it is absent, the `aiDoc` plugin is skipped entirely for that line.
22
26
  */
23
27
  function parseLine(line) {
24
28
  line = line.trim();
@@ -38,23 +42,17 @@ function parseLine(line) {
38
42
  return null;
39
43
  }
40
44
  let outputKey;
41
- let url;
42
- let template;
43
45
  // Check for key=value format
44
46
  const equalIndex = line.indexOf('=');
45
47
  if (equalIndex !== -1) {
46
48
  outputKey = line.substring(0, equalIndex).trim();
47
49
  line = line.substring(equalIndex + 1).trim();
48
50
  }
49
- // Split by comma to get URL and template
50
- const commaIndex = line.indexOf(',');
51
- if (commaIndex !== -1) {
52
- url = line.substring(0, commaIndex).trim();
53
- template = line.substring(commaIndex + 1).trim() || undefined;
54
- }
55
- else {
56
- url = line.trim();
57
- }
51
+ // Split by comma into at most three segments: url[, template][, agent]
52
+ const segments = line.split(',').map(s => s.trim()).filter(s => s !== '');
53
+ const url = segments[0] ?? '';
54
+ const template = segments[1];
55
+ const agent = segments[2];
58
56
  if (!url) {
59
57
  return null;
60
58
  }
@@ -62,6 +60,7 @@ function parseLine(line) {
62
60
  outputKey,
63
61
  url,
64
62
  template: template,
63
+ agent,
65
64
  };
66
65
  }
67
66
  /**
@@ -100,7 +99,7 @@ async function readWormaRc(projectPath) {
100
99
  if (!parsed) {
101
100
  continue;
102
101
  }
103
- const { outputKey, url, template = constant_1.PresetTemplateName.ALOVA } = parsed;
102
+ const { outputKey, url, template = constant_1.PresetTemplateName.ALOVA, agent } = parsed;
104
103
  // Determine output folder
105
104
  let output;
106
105
  if (outputKey) {
@@ -116,11 +115,18 @@ async function readWormaRc(projectPath) {
116
115
  if (!template || !PRESET_TEMPLATES[template]) {
117
116
  throw logger_1.logger.throwError(`Invalid template: ${template}. Available templates: ${Object.keys(PRESET_TEMPLATES).join(', ')}`);
118
117
  }
118
+ // Build plugin list. The aiDoc plugin is only added when an agent is
119
+ // explicitly specified on the line; otherwise no AI skill doc is generated
120
+ // or installed for this entry.
121
+ const plugins = [PRESET_TEMPLATES[template]()];
122
+ if (agent) {
123
+ plugins.push((0, plugins_1.aiDoc)({ agent }));
124
+ }
119
125
  // Build generator config
120
126
  const generatorConfig = {
121
127
  input: url,
122
128
  output,
123
- plugins: [PRESET_TEMPLATES[template](), (0, plugins_1.aiDoc)({ installSkill: true })],
129
+ plugins,
124
130
  };
125
131
  generators.push(generatorConfig);
126
132
  }
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.aiDoc = aiDoc;
7
7
  exports.parseAgentList = parseAgentList;
8
+ exports.parseAgentFile = parseAgentFile;
8
9
  const node_child_process_1 = require("node:child_process");
9
10
  const node_fs_1 = __importDefault(require("node:fs"));
10
11
  const node_module_1 = require("node:module");
@@ -13,12 +14,11 @@ const constant_1 = require("../../constant");
13
14
  const logger_1 = require("../../helper/logger");
14
15
  const template_1 = require("../../template");
15
16
  const nodeRequire = (0, node_module_1.createRequire)(__filename);
16
- const SKILLS_SUPPORTED_AGENTS_URL = 'https://www.npmjs.com/package/skills#supported-agents';
17
17
  const prefix = '[plugin: aiDoc]';
18
18
  function aiDoc(config) {
19
19
  const outputDirName = config?.outputDir ?? 'aidocs';
20
20
  const customTemplatePath = config?.template;
21
- const installSkillEnabled = config?.installSkill ?? false;
21
+ const agentValue = config?.agent;
22
22
  let capturedOutput = '';
23
23
  let capturedServerName = '';
24
24
  return {
@@ -67,9 +67,9 @@ function aiDoc(config) {
67
67
  serverName,
68
68
  },
69
69
  });
70
- if (installSkillEnabled) {
71
- const { agents } = resolveAgents(projectPath);
72
- for (const agent of agents) {
70
+ if (agentValue) {
71
+ const agentsToInstall = resolveInstallAgents(agentValue);
72
+ for (const agent of agentsToInstall) {
73
73
  installSkill(aidocsDir, agent, projectPath);
74
74
  }
75
75
  }
@@ -77,32 +77,20 @@ function aiDoc(config) {
77
77
  };
78
78
  }
79
79
  /**
80
- * Resolve the target coding agents from `.env.local` in the project root.
80
+ * Resolve the list of coding agents to install the generated skill into.
81
81
  *
82
- * Multiple agents can be configured as a comma-separated list, e.g.
83
- * `agent=cursor, claude-code, windsurf`. Both commas and surrounding
84
- * whitespace are tolerated.
82
+ * @param agent the raw `agent` config value
83
+ * - `SkillAgent` / `SkillAgent[]`: used directly as the target agent(s).
84
+ * - `string`: parsed as a comma (English or Chinese) separated agent list,
85
+ * e.g. `"cursor"` or `"cursor, claude-code"`.
85
86
  *
86
- * If the file does not exist, it will be created and `.gitignore` will be
87
- * updated to ignore `*.local` files. An error is then thrown asking the user
88
- * to set `agent=<coding-agent>` (optionally multiple, comma-separated).
89
- *
90
- * If `agent` is missing or empty, an error is thrown with guidance.
87
+ * The agent list is no longer read from `node_modules/.worma/skills.local`.
88
+ * Instead, callers pass the agent(s) explicitly via the `agent`
89
+ * option (optionally sourced from their own config file via `parseAgentFile`).
91
90
  */
92
- function resolveAgents(projectPath) {
93
- const envFilePath = node_path_1.default.join(projectPath, '.env.local');
94
- if (!node_fs_1.default.existsSync(envFilePath)) {
95
- createEnvLocalFile(envFilePath);
96
- ensureGitIgnoreLocal(projectPath);
97
- throw logger_1.logger.throwError(`${prefix}Created .env.local at project root. Please set the coding agent you are using, e.g. agent=cursor or multiple agents comma-separated: agent=cursor,claude-code. Supported agents list: ${SKILLS_SUPPORTED_AGENTS_URL}`);
98
- }
99
- const content = node_fs_1.default.readFileSync(envFilePath, 'utf-8');
100
- const raw = parseEnvValue(content, 'agent') ?? '';
101
- const agents = parseAgentList(raw);
102
- if (agents.length === 0) {
103
- throw logger_1.logger.throwError(`${prefix}Missing "agent" in .env.local at project root. Please set the coding agent you are using, e.g. agent=cursor or multiple agents comma-separated: agent=cursor,claude-code. Supported agents list: ${SKILLS_SUPPORTED_AGENTS_URL}`);
104
- }
105
- return { agents };
91
+ function resolveInstallAgents(agent) {
92
+ const raw = Array.isArray(agent) ? agent.join(',') : agent;
93
+ return parseAgentList(raw);
106
94
  }
107
95
  /**
108
96
  * Parse an agent string into a deduplicated list of trimmed agent names.
@@ -155,47 +143,45 @@ function installSkill(skillPath, agent, projectPath) {
155
143
  throw logger_1.logger.throwError(error);
156
144
  }
157
145
  }
158
- function createEnvLocalFile(envFilePath) {
159
- const content = `# Worma aiDoc skill installer configuration
160
- # Please set the coding agent(s) you are using (e.g. cursor, claude-code, windsurf).
161
- # You can configure multiple agents comma-separated, e.g. agent=cursor,claude-code.
162
- # Supported agents list: ${SKILLS_SUPPORTED_AGENTS_URL}
163
- agent=
164
- `;
165
- node_fs_1.default.writeFileSync(envFilePath, content, 'utf-8');
166
- }
167
- function ensureGitIgnoreLocal(projectPath) {
168
- const gitignorePath = node_path_1.default.join(projectPath, '.gitignore');
169
- const pattern = '*.local';
170
- let content = '';
171
- if (node_fs_1.default.existsSync(gitignorePath)) {
172
- content = node_fs_1.default.readFileSync(gitignorePath, 'utf-8');
173
- const lines = content.split(/\r?\n/);
174
- if (lines.some(line => line.trim() === pattern || line.trim() === '*.local/')) {
175
- return;
176
- }
177
- }
178
- const prefix = content === '' || content.endsWith('\n') ? '' : '\n';
179
- node_fs_1.default.writeFileSync(gitignorePath, `${content}${prefix}${pattern}\n`, 'utf-8');
180
- }
181
- function parseEnvValue(content, key) {
182
- const lines = content.split(/\r?\n/);
183
- for (const line of lines) {
146
+ /**
147
+ * Parse a `key=value` configuration file (same format as an environment file).
148
+ *
149
+ * Lines starting with `#` are treated as comments and ignored; blank lines and
150
+ * lines without `=` are skipped. Surrounding single/double quotes around values
151
+ * are stripped. Returns a map of keys to their (string) values.
152
+ *
153
+ * When `filePath` is omitted, the file is read from `.wormaagent.local` in the
154
+ * current working directory (project root).
155
+ *
156
+ * This makes it easy to keep the target coding agent(s) in a config file and
157
+ * feed them into the `agent` option:
158
+ *
159
+ * @example
160
+ * ```ts
161
+ * // .wormaagent.local -> agent=cursor, claude-code
162
+ * const cfg = parseAgentFile() // reads ./.wormaagent.local by default
163
+ * aiDoc({ agent: cfg.agent })
164
+ * ```
165
+ */
166
+ function parseAgentFile(filePath) {
167
+ const target = filePath ?? node_path_1.default.resolve(process.cwd(), '.wormaagent.local');
168
+ const content = node_fs_1.default.readFileSync(target, 'utf-8');
169
+ const result = {};
170
+ for (const line of content.split(/\r?\n/)) {
184
171
  const trimmed = line.trim();
185
- if (trimmed.startsWith('#') || !trimmed.includes('=')) {
172
+ if (trimmed === '' || trimmed.startsWith('#') || !trimmed.includes('=')) {
186
173
  continue;
187
174
  }
188
175
  const eqIndex = trimmed.indexOf('=');
189
- const k = trimmed.slice(0, eqIndex).trim();
190
- let v = trimmed.slice(eqIndex + 1).trim();
176
+ const key = trimmed.slice(0, eqIndex).trim();
177
+ let value = trimmed.slice(eqIndex + 1).trim();
191
178
  // Remove surrounding quotes if present
192
- if ((v.startsWith('"') && v.endsWith('"')) || (v.startsWith('\'') && v.endsWith('\''))) {
193
- v = v.slice(1, -1);
194
- }
195
- if (k === key) {
196
- return v;
179
+ if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith('\'') && value.endsWith('\''))) {
180
+ value = value.slice(1, -1);
197
181
  }
182
+ if (key)
183
+ result[key] = value;
198
184
  }
199
- return undefined;
185
+ return result;
200
186
  }
201
187
  exports.default = aiDoc;
@@ -18,7 +18,7 @@ generator: [
18
18
  * swagger platform plugin: auto-resolves OpenAPI file URLs from the base URL.
19
19
  * The base URL is passed as the plugin argument (not config.input).
20
20
  */
21
- plugins: [swagger('http://localhost:3000'), aiDoc({ installSkill: true }), {{{templateCall}}}],
21
+ plugins: [swagger('http://localhost:3000'), aiDoc({ agent: 'codex' }), {{{templateCall}}}],
22
22
 
23
23
  /**
24
24
  * the mediaType of the generated response data. default is `application/json`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wormajs",
3
- "version": "0.2.7",
3
+ "version": "0.3.0",
4
4
  "description": "A modern OpenAPI code generator - Generate type-safe API clients from OpenAPI specs",
5
5
  "author": "worma",
6
6
  "license": "MIT",
@@ -424,10 +424,30 @@ export interface TemplateData {
424
424
  * });
425
425
  */
426
426
  export declare function createPlugin<T extends any[]>(plugin: (...args: T) => ApiPlugin): (...args: T) => ApiPlugin;
427
+ /**
428
+ * Coding agents that the generated skill can be installed into.
429
+ *
430
+ * The `skills` package (`https://www.npmjs.com/package/skills`) is CLI-only and
431
+ * ships no TypeScript types, so this union mirrors the agent names it supports
432
+ * (its documented "supported agents" list). The transitive `@vercel/detect-agent`
433
+ * package does export a `KnownAgentNames` type, but it only covers a small subset
434
+ * of agents (e.g. it is missing `claude-code` and `windsurf`), so it cannot be
435
+ * reused directly here.
436
+ */
437
+ export type SkillAgent = "aider-desk" | "amp" | "antigravity" | "antigravity-cli" | "astrbot" | "augment" | "autohand-code" | "bob" | "claude-code" | "cline" | "codearts-agent" | "codebuddy" | "codemaker" | "codestudio" | "codex" | "command-code" | "continue" | "cortex" | "crush" | "cursor" | "deepagents" | "devin" | "dexto" | "droid" | "eve" | "firebender" | "forgecode" | "gemini-cli" | "github-copilot" | "goose" | "hermes-agent" | "iflow-cli" | "inference-sh" | "jazz" | "junie" | "kilo" | "kiro-cli" | "kimi-code-cli" | "kode" | "lingma" | "loaf" | "mcpjam" | "mistral-vibe" | "moxby" | "mux" | "ona" | "opencode" | "openhands" | "openclaw" | "pi" | "pochi" | "promptscript" | "qoder" | "qoder-cn" | "qwen-code" | "reasonix" | "replit" | "rovodev" | "roo" | "tabnine-cli" | "terramind" | "tinycloud" | "trae" | "trae-cn" | "universal" | "warp" | "windsurf" | "zed" | "zencoder" | "zenflow" | "neovate" | "adal";
427
438
  export interface AiDocConfig {
428
439
  template?: string;
429
440
  outputDir?: string;
430
- installSkill?: boolean;
441
+ /**
442
+ * Which coding agent(s) to install the generated skill into.
443
+ * - omitted: do NOT install the skill.
444
+ * - `SkillAgent` / `SkillAgent[]`: install to the given agent(s) directly.
445
+ * - `string`: comma (English or Chinese) separated agent names, used directly
446
+ * as the target agent(s), e.g. `"cursor"` or `"cursor, claude-code"`.
447
+ * This is handy when the agent list comes from a config file parsed via
448
+ * `parseAgentFile`, e.g. `aiDoc({ agent: parseAgentFile('.myrc').agent })`.
449
+ */
450
+ agent?: SkillAgent | (SkillAgent | (string & {}))[] | (string & {});
431
451
  }
432
452
  export declare function aiDoc(config?: AiDocConfig): ApiPlugin;
433
453
  /**
@@ -438,6 +458,27 @@ export declare function aiDoc(config?: AiDocConfig): ApiPlugin;
438
458
  * either side. Empty entries are ignored.
439
459
  */
440
460
  export declare function parseAgentList(raw: string): string[];
461
+ /**
462
+ * Parse a `key=value` configuration file (same format as an environment file).
463
+ *
464
+ * Lines starting with `#` are treated as comments and ignored; blank lines and
465
+ * lines without `=` are skipped. Surrounding single/double quotes around values
466
+ * are stripped. Returns a map of keys to their (string) values.
467
+ *
468
+ * When `filePath` is omitted, the file is read from `.wormaagent.local` in the
469
+ * current working directory (project root).
470
+ *
471
+ * This makes it easy to keep the target coding agent(s) in a config file and
472
+ * feed them into the `agent` option:
473
+ *
474
+ * @example
475
+ * ```ts
476
+ * // .wormaagent.local -> agent=cursor, claude-code
477
+ * const cfg = parseAgentFile() // reads ./.wormaagent.local by default
478
+ * aiDoc({ agent: cfg.agent })
479
+ * ```
480
+ */
481
+ export declare function parseAgentFile(filePath?: string): Record<string, string>;
441
482
  export type ScopeType = "ALL" | "SELECTED_ENDPOINTS" | "SELECTED_TAGS" | "SELECTED_FOLDERS";
442
483
  export interface APIFoxBody {
443
484
  scope?: {