opencode-idea 0.0.18 → 0.0.20
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 +215 -51
- package/package.json +1 -1
- package/skills/idea-run-config-manager/SKILL.md +62 -0
- package/skills/idea-run-config-manager/assets/AGENTS.template.md +58 -0
- package/skills/idea-run-config-manager/assets/types/go-build.md +44 -0
- package/skills/idea-run-config-manager/assets/types/gradle.md +57 -0
- package/skills/idea-run-config-manager/assets/types/maven.md +61 -0
- package/skills/idea-run-config-manager/assets/types/node.md +30 -0
- package/skills/idea-run-config-manager/assets/types/npm.md +58 -0
- package/skills/idea-run-config-manager/assets/types/python.md +59 -0
- package/skills/idea-run-config-manager/assets/types/shell.md +77 -0
- package/skills/idea-standalone/SKILL.md +1 -1
- package/skills/idea-standalone/assets/AGENTS.template.md +11 -7
- package/src/ide-guidance.js +85 -10
- package/src/ide-guidance.template.md +53 -6
- package/src/ide-projects.js +1 -1
- package/src/mcp/enhance.js +82 -12
- package/src/mcp/idea.js +2 -2
- package/src/plugin.js +39 -38
- package/src/skill/loader.js +43 -3
package/src/plugin.js
CHANGED
|
@@ -14,17 +14,18 @@
|
|
|
14
14
|
// its own project through the `IJ_MCP_SERVER_PROJECT_PATH` header; injects
|
|
15
15
|
// per-project direct-call guidance; and hides the IDE tools that must not
|
|
16
16
|
// reach the model (for every prefix);
|
|
17
|
-
// 2. registers `/open-in-
|
|
17
|
+
// 2. registers `/open-in-ide` (接入 root 与其全部子项目), `/delete-in-ide`
|
|
18
18
|
// (删掉整个配置) and `/add-sub-module-in-ide <路径>` (登记一个子项目),
|
|
19
19
|
// plus the `configure_ide_project` tool the MODEL calls to write the IDE a
|
|
20
20
|
// user picked into the config;
|
|
21
|
-
// 3. registers the `
|
|
21
|
+
// 3. registers the shipped skills (`skills/<id>/SKILL.md`) — marked projects
|
|
22
|
+
// only, and each one also gated on the IDE tools it drives.
|
|
22
23
|
//
|
|
23
24
|
// A root that was never configured is left untouched. Everything is manual:
|
|
24
25
|
// setup does NOT probe or connect any IDE. The commands probe, open the IDE, and
|
|
25
26
|
// load the MCP servers + guidance on demand.
|
|
26
27
|
//
|
|
27
|
-
// `/open-in-
|
|
28
|
+
// `/open-in-ide` opens each configured project in ITS OWN IDE, waits until its
|
|
28
29
|
// MCP endpoint is ready, registers it under its prefix, and keeps the config in
|
|
29
30
|
// `ctx.storage`. A live endpoint alone does NOT mean our project is open there
|
|
30
31
|
// (the running IDE may have a different project open), so the open flow asks the
|
|
@@ -38,7 +39,7 @@
|
|
|
38
39
|
// after OpenCode evicted the project's Location services (the Location's
|
|
39
40
|
// LayerMap uses `idleTimeToLive: "60 minutes"`) or after the process
|
|
40
41
|
// restarted — setup deliberately does not connect, so without this the
|
|
41
|
-
// session would stay tool-less until the user ran `/open-in-
|
|
42
|
+
// session would stay tool-less until the user ran `/open-in-ide` by hand.
|
|
42
43
|
// `prompt` is awaited before the request is built, so the message that
|
|
43
44
|
// triggers it already carries the tools;
|
|
44
45
|
// * the `context` hook, when a request that HAD IDE tools no longer does;
|
|
@@ -54,7 +55,6 @@ import { existsSync } from 'node:fs';
|
|
|
54
55
|
import { resolve } from 'node:path';
|
|
55
56
|
|
|
56
57
|
import { callTool, serverConfig } from './mcp/idea.js';
|
|
57
|
-
import { runLifecycleCapabilities } from './mcp/enhance.js';
|
|
58
58
|
import { applyIdeToolDefaults, hideForcedArguments } from './mcp/idea-tool-defaults.js';
|
|
59
59
|
import { currentProjectPath } from './project.js';
|
|
60
60
|
import {
|
|
@@ -405,26 +405,26 @@ export function ideFeedback(result) {
|
|
|
405
405
|
return list ? `IDE MCP 已连接并接入:${list}。` : 'IDE MCP 已连接。';
|
|
406
406
|
}
|
|
407
407
|
case 'tools-loading':
|
|
408
|
-
return 'IDE MCP 已连接,但原生 IDE 工具列表尚未完成注册。请稍后再次执行 /open-in-
|
|
408
|
+
return 'IDE MCP 已连接,但原生 IDE 工具列表尚未完成注册。请稍后再次执行 /open-in-ide。';
|
|
409
409
|
case 'mcp-unavailable': {
|
|
410
410
|
const where = nameOf(result?.project);
|
|
411
411
|
const ide = appOf(result?.project);
|
|
412
412
|
return result?.opened
|
|
413
|
-
? `已请求打开/激活 ${where} 并等待 MCP 服务启动,但未检测到 MCP 服务。请在 ${ide} 中开启 MCP 服务(Settings → MCP Server,需 2026.2+)并启用 Brave Mode,然后再次执行 /open-in-
|
|
414
|
-
: `未检测到 ${where} 的 MCP 服务。请确认已打开,在 Settings → MCP Server 开启 MCP 服务并启用 Brave Mode,然后再次执行 /open-in-
|
|
413
|
+
? `已请求打开/激活 ${where} 并等待 MCP 服务启动,但未检测到 MCP 服务。请在 ${ide} 中开启 MCP 服务(Settings → MCP Server,需 2026.2+)并启用 Brave Mode,然后再次执行 /open-in-ide。`
|
|
414
|
+
: `未检测到 ${where} 的 MCP 服务。请确认已打开,在 Settings → MCP Server 开启 MCP 服务并启用 Brave Mode,然后再次执行 /open-in-ide。`;
|
|
415
415
|
}
|
|
416
416
|
case 'launch-failed':
|
|
417
|
-
return `无法拉起 ${nameOf(result?.project)}(未找到应用或启动失败)。请确认它已安装 —— 没装的话先装上,再重试 /open-in-
|
|
417
|
+
return `无法拉起 ${nameOf(result?.project)}(未找到应用或启动失败)。请确认它已安装 —— 没装的话先装上,再重试 /open-in-ide。`;
|
|
418
418
|
case 'disabled':
|
|
419
|
-
return '插件已通过 openInIde: false 关闭 IDE 启动。请手动打开 IDE,在 Settings → MCP Server 开启 MCP 服务并启用 Brave Mode,然后重试 /open-in-
|
|
419
|
+
return '插件已通过 openInIde: false 关闭 IDE 启动。请手动打开 IDE,在 Settings → MCP Server 开启 MCP 服务并启用 Brave Mode,然后重试 /open-in-ide。';
|
|
420
420
|
case 'manual-launch':
|
|
421
|
-
return '当前系统不支持自动打开 IDE(仅 macOS 实现了启动)。请手动打开 IDE,在 Settings → MCP Server 开启 MCP 服务并启用 Brave Mode,然后再次执行 /open-in-
|
|
421
|
+
return '当前系统不支持自动打开 IDE(仅 macOS 实现了启动)。请手动打开 IDE,在 Settings → MCP Server 开启 MCP 服务并启用 Brave Mode,然后再次执行 /open-in-ide。';
|
|
422
422
|
case 'deleted':
|
|
423
|
-
return '已删除本项目的 IDE 配置:已注销全部 IDE MCP 并清除配置。如需重新接入,请执行 /open-in-
|
|
423
|
+
return '已删除本项目的 IDE 配置:已注销全部 IDE MCP 并清除配置。如需重新接入,请执行 /open-in-ide。';
|
|
424
424
|
case 'bad-argument':
|
|
425
425
|
return `命令参数有误:${result?.message ?? ''}`;
|
|
426
426
|
case 'not-configured':
|
|
427
|
-
return '当前项目还没有 IDE 配置。请先执行 /open-in-
|
|
427
|
+
return '当前项目还没有 IDE 配置。请先执行 /open-in-ide 接入根项目,再登记子项目。';
|
|
428
428
|
case 'invalid-subproject':
|
|
429
429
|
return `无法登记子项目:${result?.message ?? ''}`;
|
|
430
430
|
case 'needs-ide':
|
|
@@ -435,7 +435,7 @@ export function ideFeedback(result) {
|
|
|
435
435
|
}
|
|
436
436
|
|
|
437
437
|
/**
|
|
438
|
-
* The text the MODEL sees for `/open-in-
|
|
438
|
+
* The text the MODEL sees for `/open-in-ide` feedback. It is explicitly a
|
|
439
439
|
* notification, not a task: the model must only acknowledge it. The user sees
|
|
440
440
|
* the clean notice instead, via `metadata.displayText`.
|
|
441
441
|
*/
|
|
@@ -850,7 +850,7 @@ export default {
|
|
|
850
850
|
* `open -a` only requests activation/opening. A cold IDE needs time before
|
|
851
851
|
* its bundled MCP server listens, so poll until the endpoint appears AND
|
|
852
852
|
* answers for THIS project. This is the step that makes a single
|
|
853
|
-
* `/open-in-
|
|
853
|
+
* `/open-in-ide` enough on a cold start.
|
|
854
854
|
*
|
|
855
855
|
* @param {{ path: string, app: string }} project
|
|
856
856
|
* @returns {Promise<number | undefined>}
|
|
@@ -946,7 +946,7 @@ export default {
|
|
|
946
946
|
};
|
|
947
947
|
|
|
948
948
|
/**
|
|
949
|
-
* `/open-in-
|
|
949
|
+
* `/open-in-ide` 的主流程:让每个已登记项目的 IDE 都就绪,然后把它们注册进
|
|
950
950
|
* OpenCode。
|
|
951
951
|
*
|
|
952
952
|
* 每个项目各自拉起**自己的** IDE —— root 在 IDEA、某子项目在 PyCharm 是常态,
|
|
@@ -970,7 +970,7 @@ export default {
|
|
|
970
970
|
let toolsReady = false;
|
|
971
971
|
try {
|
|
972
972
|
// 重新探测一次并整体注册:ensureProject 只保证 IDE 就绪,注册要按"当前真正
|
|
973
|
-
// 打开着哪些项目"重来一遍,这样 `/open-in-
|
|
973
|
+
// 打开着哪些项目"重来一遍,这样 `/open-in-ide` 每次都是同一套结果。
|
|
974
974
|
toolsReady = (await reconcile({ probeIde: true })) === true;
|
|
975
975
|
} catch {
|
|
976
976
|
// capability load is best effort; feedback still matters
|
|
@@ -987,7 +987,7 @@ export default {
|
|
|
987
987
|
* The `context` hook latches the rebuild the moment a request shows the tools
|
|
988
988
|
* are gone, but a latch alone only fires on the NEXT user message — one whole
|
|
989
989
|
* turn later, which is exactly the gap that made a dead session sit there
|
|
990
|
-
* until `/open-in-
|
|
990
|
+
* until `/open-in-ide` was run by hand. This runs out of band (it never
|
|
991
991
|
* blocks the request) so the next request — the following agent-loop step, or
|
|
992
992
|
* any later message — already carries the tools again.
|
|
993
993
|
*
|
|
@@ -1065,20 +1065,21 @@ export default {
|
|
|
1065
1065
|
const isSameSkillSet = (skills, ids) =>
|
|
1066
1066
|
skills.length === ids.length && skills.every((skill, index) => skill.id === ids[index]);
|
|
1067
1067
|
|
|
1068
|
-
// The shipped skills are static knowledge (`skills/<id>/SKILL.md`)
|
|
1069
|
-
//
|
|
1070
|
-
//
|
|
1071
|
-
//
|
|
1068
|
+
// The shipped skills are static knowledge (`skills/<id>/SKILL.md`) that write
|
|
1069
|
+
// a project's own rules into its `AGENTS.md`: `idea-standalone` fixes how
|
|
1070
|
+
// resident apps are started, `idea-run-config-manager` fixes which repeated
|
|
1071
|
+
// flows are solidified into configurations. They pass two independent gates,
|
|
1072
1072
|
// because a skill can be wrong twice over:
|
|
1073
1073
|
//
|
|
1074
1074
|
// * the project config — an unrelated project never sees an IDE skill;
|
|
1075
1075
|
// * the tools the IDE actually serves — a skill that teaches a call the
|
|
1076
1076
|
// model cannot make is not knowledge, it is a failed call waiting to
|
|
1077
|
-
// happen.
|
|
1078
|
-
//
|
|
1079
|
-
// see
|
|
1080
|
-
// (`requires:`), as a SUFFIX — tool
|
|
1081
|
-
// a hard-coded `idea_...` could only
|
|
1077
|
+
// happen. Both skills drive tools that only the companion plugin
|
|
1078
|
+
// `idea-mcp-enhance` serves (rerun for standalone, create for the
|
|
1079
|
+
// manager), so a stock IDE must not see them at all. Each skill declares
|
|
1080
|
+
// its own dependency in its frontmatter (`requires:`), as a SUFFIX — tool
|
|
1081
|
+
// names carry a per-project prefix, so a hard-coded `idea_...` could only
|
|
1082
|
+
// ever match one project.
|
|
1082
1083
|
//
|
|
1083
1084
|
// The catalog is unknown at setup: the config is read from storage offline,
|
|
1084
1085
|
// before anything has connected to the IDE. So this is a reconcile rather
|
|
@@ -1089,7 +1090,7 @@ export default {
|
|
|
1089
1090
|
// Deliberately NOT wrapped in a catch: these are core features of the plugin,
|
|
1090
1091
|
// so a rejected definition must fail loudly. Swallowing it would ship a
|
|
1091
1092
|
// silently incomplete plugin — which is exactly how 0.0.6 lost
|
|
1092
|
-
// `/open-in-
|
|
1093
|
+
// `/open-in-ide` without anyone noticing until the command was gone.
|
|
1093
1094
|
const syncSkillsOnce = async () => {
|
|
1094
1095
|
if (typeof ctx.skill?.transform !== 'function') return;
|
|
1095
1096
|
|
|
@@ -1138,7 +1139,7 @@ export default {
|
|
|
1138
1139
|
};
|
|
1139
1140
|
// A project configured in an earlier run gets its skills as soon as the IDE
|
|
1140
1141
|
// has answered; a project configured for the first time gets them when
|
|
1141
|
-
// `/open-in-
|
|
1142
|
+
// `/open-in-ide` runs (both paths go through `writeConfig` / this setup hook).
|
|
1142
1143
|
await syncSkills();
|
|
1143
1144
|
|
|
1144
1145
|
/**
|
|
@@ -1227,7 +1228,7 @@ export default {
|
|
|
1227
1228
|
return { content: `已把根项目配置为 ${IDE_LABELS[ide]} 并接入。` };
|
|
1228
1229
|
}
|
|
1229
1230
|
if (!current) {
|
|
1230
|
-
return { content: '当前项目还没有 IDE 配置。请先执行 /open-in-
|
|
1231
|
+
return { content: '当前项目还没有 IDE 配置。请先执行 /open-in-ide。' };
|
|
1231
1232
|
}
|
|
1232
1233
|
const absPath = resolve(projectPath, rawPath);
|
|
1233
1234
|
if (!existsSync(absPath)) {
|
|
@@ -1268,15 +1269,15 @@ export default {
|
|
|
1268
1269
|
execute: async (args) => configureIdeProject(args),
|
|
1269
1270
|
});
|
|
1270
1271
|
|
|
1271
|
-
// `/open-in-
|
|
1272
|
+
// `/open-in-ide` 接入 root 与其全部子项目;`/add-sub-module-in-ide <路径>` 登记
|
|
1272
1273
|
// 一个子项目(IDE 由模型问用户后写回);`/delete-in-ide` 删除整个配置。
|
|
1273
1274
|
//
|
|
1274
|
-
//
|
|
1275
|
-
//
|
|
1275
|
+
// 三个名字都带 `-ide` 而不是 `-idea`:这里说的是"接入 IDE",与具体产品无关 —— root 可能是
|
|
1276
|
+
// IDEA、子项目可能是 PyCharm,和工具前缀(`ide_*` / `<目录名>_*`)是同一套口径。
|
|
1276
1277
|
if (typeof ctx.command?.transform === 'function') {
|
|
1277
1278
|
commandRegistration = await ctx.command.transform((editor) => {
|
|
1278
1279
|
editor.add({
|
|
1279
|
-
name: 'open-in-
|
|
1280
|
+
name: 'open-in-ide',
|
|
1280
1281
|
description: '接入当前项目:root 及其全部已登记子项目,各自用配置好的 IDE 打开',
|
|
1281
1282
|
execute: async (input) => {
|
|
1282
1283
|
const parsed = parseIdeArgument(input?.prompt?.text);
|
|
@@ -1350,7 +1351,7 @@ export default {
|
|
|
1350
1351
|
execute: async (input) => {
|
|
1351
1352
|
await writeConfig(null);
|
|
1352
1353
|
// 注销全部 server。走串行化的 reconcile,所以在飞的重建(另一个
|
|
1353
|
-
// `/open-in-
|
|
1354
|
+
// `/open-in-ide`、请求期自愈)不会在配置已删之后再注册回来。
|
|
1354
1355
|
await reconcile({ probeIde: false });
|
|
1355
1356
|
await sendFeedback(input, { status: 'deleted' });
|
|
1356
1357
|
},
|
|
@@ -1420,8 +1421,8 @@ export default {
|
|
|
1420
1421
|
// they are actually in this request.
|
|
1421
1422
|
if (!serving) return;
|
|
1422
1423
|
// 逐项目注入:每个项目那一节只写它自己的前缀,模型不需要自己换算。
|
|
1423
|
-
//
|
|
1424
|
-
// 所以一个项目装了 enhance
|
|
1424
|
+
// 配套插件 `idea-mcp-enhance` 的两组工具(run-lifecycle / deployment)也按每个项目分别判断,
|
|
1425
|
+
// 所以一个项目装了 enhance、另一个没装时,各节的内容本来就不一样。
|
|
1425
1426
|
appendIdeGuidance(event?.system, currentProjects(), { tools });
|
|
1426
1427
|
});
|
|
1427
1428
|
|
|
@@ -1434,7 +1435,7 @@ export default {
|
|
|
1434
1435
|
// * `registrations.length === 0` — 本插件实例压根没有注册任何 IDE server,这
|
|
1435
1436
|
// 正是 OpenCode 回收 Location 服务(LayerMap idleTimeToLive = 60 分钟)
|
|
1436
1437
|
// 或进程重启后新实例的样子。setup 故意不连接,没有这一步会话就会永久
|
|
1437
|
-
// 没有工具,只能靠用户手敲 `/open-in-
|
|
1438
|
+
// 没有工具,只能靠用户手敲 `/open-in-ide`。
|
|
1438
1439
|
//
|
|
1439
1440
|
// 只探测、不启动 IDE:走 `reconcile({ probeIde: true })`,而不是会 `open -a`
|
|
1440
1441
|
// 的 `reviveIde()` —— 一条用户消息不该把 IDE 拉起来。
|
package/src/skill/loader.js
CHANGED
|
@@ -27,6 +27,13 @@
|
|
|
27
27
|
// and deliberately kept OUT of the registered definition — the caller filters on
|
|
28
28
|
// it instead (see `readSkills`), because a skill whose tools are absent must not
|
|
29
29
|
// be registered at all.
|
|
30
|
+
//
|
|
31
|
+
// The other key is OpenCode's own, written in the portable frontmatter spelling
|
|
32
|
+
// OpenCode documents: a nested `metadata:` block holding
|
|
33
|
+
// `opencode/autoinvoke: false`. It is translated to the definition's
|
|
34
|
+
// `autoinvoke` — the name the registration API uses — which is what makes a
|
|
35
|
+
// skill user-invoked only: OpenCode keeps it registered and loadable on request,
|
|
36
|
+
// but leaves it out of the model's available list.
|
|
30
37
|
|
|
31
38
|
import { readFileSync, readdirSync } from 'node:fs';
|
|
32
39
|
import { fileURLToPath } from 'node:url';
|
|
@@ -87,11 +94,37 @@ function splitList(value) {
|
|
|
87
94
|
.filter((entry) => entry !== '');
|
|
88
95
|
}
|
|
89
96
|
|
|
97
|
+
/**
|
|
98
|
+
* Read `opencode/autoinvoke` out of the frontmatter, or `undefined` when absent.
|
|
99
|
+
*
|
|
100
|
+
* The spelling is OpenCode's portable one — a nested `metadata:` block holding
|
|
101
|
+
* `opencode/autoinvoke: false`. That shape is exactly what the flat reader above
|
|
102
|
+
* already produces: the indented line trims to `opencode/autoinvoke: false` and
|
|
103
|
+
* lands under that namespaced key, while the bare `metadata:` line comes out
|
|
104
|
+
* empty and is ignored. One nesting level is therefore supported on purpose, not
|
|
105
|
+
* by accident.
|
|
106
|
+
*
|
|
107
|
+
* The value must be `true` or `false`. Anything else throws: a typo that
|
|
108
|
+
* silently left the skill model-invocable is the failure this whole mechanism
|
|
109
|
+
* exists to prevent, so it must not pass quietly.
|
|
110
|
+
*
|
|
111
|
+
* @param {Record<string, string>} fields
|
|
112
|
+
* @param {string} source Used in error messages.
|
|
113
|
+
* @returns {boolean | undefined}
|
|
114
|
+
*/
|
|
115
|
+
export function readAutoinvoke(fields, source) {
|
|
116
|
+
const raw = fields['opencode/autoinvoke'];
|
|
117
|
+
if (raw === undefined) return undefined;
|
|
118
|
+
if (raw === 'true') return true;
|
|
119
|
+
if (raw === 'false') return false;
|
|
120
|
+
throw new Error(`${source}: "opencode/autoinvoke" must be true or false, got "${raw}"`);
|
|
121
|
+
}
|
|
122
|
+
|
|
90
123
|
/**
|
|
91
124
|
* Load one skill directory into a `Skill.Info`-shaped definition.
|
|
92
125
|
*
|
|
93
126
|
* @param {string} id Directory name, used as the skill id.
|
|
94
|
-
* @returns {{ id: string, name: string, description: string, path: string, content: string }}
|
|
127
|
+
* @returns {{ id: string, name: string, description: string, autoinvoke?: boolean, path: string, content: string }}
|
|
95
128
|
*/
|
|
96
129
|
export function readSkill(id) {
|
|
97
130
|
return readSkillPackage(id).skill;
|
|
@@ -104,8 +137,13 @@ export function readSkill(id) {
|
|
|
104
137
|
* while `requires` is this package's own metadata and must NOT be registered —
|
|
105
138
|
* it is what the caller filters on.
|
|
106
139
|
*
|
|
140
|
+
* `autoinvoke` is OpenCode's, so it goes the other way: it ships in the
|
|
141
|
+
* frontmatter as `opencode/autoinvoke` and is translated onto the definition,
|
|
142
|
+
* where it picks user-invoked-only over model-invocable. It is only set when the
|
|
143
|
+
* frontmatter asks for it, so a skill that says nothing keeps the plain shape.
|
|
144
|
+
*
|
|
107
145
|
* @param {string} id Directory name, used as the skill id.
|
|
108
|
-
* @returns {{ skill: { id: string, name: string, description: string, path: string, content: string }, requires: string[] }}
|
|
146
|
+
* @returns {{ skill: { id: string, name: string, description: string, autoinvoke?: boolean, path: string, content: string }, requires: string[] }}
|
|
109
147
|
*/
|
|
110
148
|
export function readSkillPackage(id) {
|
|
111
149
|
const file = new URL(`${id}/${SKILL_FILE_NAME}`, SKILLS_DIR);
|
|
@@ -116,11 +154,13 @@ export function readSkillPackage(id) {
|
|
|
116
154
|
if (!description) {
|
|
117
155
|
throw new Error(`${source}: frontmatter needs a "description", otherwise the skill is never offered`);
|
|
118
156
|
}
|
|
157
|
+
const autoinvoke = readAutoinvoke(fields, source);
|
|
119
158
|
return {
|
|
120
159
|
skill: {
|
|
121
160
|
id,
|
|
122
161
|
name: fields.name || id,
|
|
123
162
|
description,
|
|
163
|
+
...(autoinvoke === undefined ? {} : { autoinvoke }),
|
|
124
164
|
path: fileURLToPath(file),
|
|
125
165
|
content: text.replace(FRONTMATTER, '').replace(/^\s+/, '').trimEnd(),
|
|
126
166
|
},
|
|
@@ -131,7 +171,7 @@ export function readSkillPackage(id) {
|
|
|
131
171
|
/**
|
|
132
172
|
* Every shipped skill, sorted by id, each with the tools it requires.
|
|
133
173
|
*
|
|
134
|
-
* @returns {Array<{ skill: { id: string, name: string, description: string, path: string, content: string }, requires: string[] }>}
|
|
174
|
+
* @returns {Array<{ skill: { id: string, name: string, description: string, autoinvoke?: boolean, path: string, content: string }, requires: string[] }>}
|
|
135
175
|
*/
|
|
136
176
|
export function readSkills() {
|
|
137
177
|
return readdirSync(SKILLS_DIR, { withFileTypes: true })
|