@skillfm/local 2.0.1 → 2.0.4
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 +7 -2
- package/dist/agent-hints.d.ts +11 -15
- package/dist/agent-hints.d.ts.map +1 -1
- package/dist/agent-hints.js +75 -54
- package/dist/agent-hints.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,9 +11,14 @@ This package sidesteps the problem: instead of asking the agent to install a new
|
|
|
11
11
|
## Install and run
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
|
-
npx
|
|
14
|
+
npx --yes --package @skillfm/local@latest skillfm-local start
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
+
> Why the verbose `--package` form? This npm package ships two CLI entry points
|
|
18
|
+
> (`skillfm-local` + `skillfm-guard` for hook-based harness enforcement). Since
|
|
19
|
+
> the package name's tail `local` is a shell reserved word, we cannot expose it
|
|
20
|
+
> as a bin shortcut. The above `--package` form tells npx exactly which bin to run.
|
|
21
|
+
|
|
17
22
|
Output is a single line of JSON the agent can parse:
|
|
18
23
|
|
|
19
24
|
```json
|
|
@@ -57,7 +62,7 @@ On successful `/activate/verify`, the brain_key is persisted to `~/.skillfm/conf
|
|
|
57
62
|
|
|
58
63
|
## Agent-native flow in plain words
|
|
59
64
|
|
|
60
|
-
1. Agent spawns the sidecar: `npx
|
|
65
|
+
1. Agent spawns the sidecar: `npx --yes --package @skillfm/local@latest skillfm-local start &`
|
|
61
66
|
2. Agent reads `~/.skillfm/local.json` to get the URL.
|
|
62
67
|
3. Agent asks the user for their email and POSTs `/activate/request`.
|
|
63
68
|
4. The backend emails a 6-digit code. Agent asks the user for the code.
|
package/dist/agent-hints.d.ts
CHANGED
|
@@ -1,29 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Agent Hint
|
|
2
|
+
* Agent Hint — sidecar → agent 的指令文案 (双语并列)
|
|
3
3
|
*
|
|
4
|
-
* 场景: 用户跑 `npx -y @skillfm/local@latest start` 或 POST /activate/poll
|
|
5
|
-
* sidecar 返回的 JSON 里带 `hint_for_agent
|
|
6
|
-
* 对用户说什么.
|
|
4
|
+
* 场景: 用户跑 `npx -y @skillfm/local@latest start` 或 POST /activate/poll
|
|
5
|
+
* 成功后, sidecar 返回的 JSON 里带 `hint_for_agent`, agent 读后决定下一步动作 +
|
|
6
|
+
* 对用户说什么.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
8
|
+
* 设计 (2026-04-19 方案 v2):
|
|
9
|
+
* 首次安装的 hint 文本量不大, 直接**中英并列**. agent 会根据用户发来的消息
|
|
10
|
+
* 语言自动选对应语言回复 — 不需要服务端 detectLang, 更鲁棒
|
|
11
|
+
* (不依赖 env / Intl / locale 判断, 不会踩 macOS LANG=en_US 但系统中文 的坑).
|
|
10
12
|
*
|
|
11
|
-
*
|
|
12
|
-
* docs/prd/PRD-BSO-M9-HARNESS-HOOKS.md (双语 priming 原则延伸到 hint)
|
|
13
|
+
* 每条 hint 尾部明示: [Reply in the user's language | 请用用户所用的语言回复].
|
|
13
14
|
*/
|
|
14
|
-
import { type Lang } from './lang.js';
|
|
15
15
|
export type AgentHintKey = 'activation_success' | 'running_activated' | 'running_not_activated';
|
|
16
16
|
interface AgentHintVars {
|
|
17
17
|
/** sidecar 运行地址 (running_* 用) */
|
|
18
18
|
url?: string;
|
|
19
19
|
}
|
|
20
20
|
/**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* @param key hint 类型
|
|
24
|
-
* @param vars 模板变量 (如 url)
|
|
25
|
-
* @param lang 可选, 显式指定语言; 不传则走 detectLang(process.env)
|
|
21
|
+
* 返回指定 key 的双语并列 hint. Agent 按用户消息语言自动匹配回复.
|
|
26
22
|
*/
|
|
27
|
-
export declare function agentHint(key: AgentHintKey, vars?: AgentHintVars
|
|
23
|
+
export declare function agentHint(key: AgentHintKey, vars?: AgentHintVars): string;
|
|
28
24
|
export {};
|
|
29
25
|
//# sourceMappingURL=agent-hints.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-hints.d.ts","sourceRoot":"","sources":["../src/agent-hints.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"agent-hints.d.ts","sourceRoot":"","sources":["../src/agent-hints.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,MAAM,YAAY,GACpB,oBAAoB,GACpB,mBAAmB,GACnB,uBAAuB,CAAC;AAE5B,UAAU,aAAa;IACrB,iCAAiC;IACjC,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAwED;;GAEG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,YAAY,EAAE,IAAI,GAAE,aAAkB,GAAG,MAAM,CAY7E"}
|
package/dist/agent-hints.js
CHANGED
|
@@ -1,66 +1,87 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Agent Hint
|
|
2
|
+
* Agent Hint — sidecar → agent 的指令文案 (双语并列)
|
|
3
3
|
*
|
|
4
|
-
* 场景: 用户跑 `npx -y @skillfm/local@latest start` 或 POST /activate/poll
|
|
5
|
-
* sidecar 返回的 JSON 里带 `hint_for_agent
|
|
6
|
-
* 对用户说什么.
|
|
4
|
+
* 场景: 用户跑 `npx -y @skillfm/local@latest start` 或 POST /activate/poll
|
|
5
|
+
* 成功后, sidecar 返回的 JSON 里带 `hint_for_agent`, agent 读后决定下一步动作 +
|
|
6
|
+
* 对用户说什么.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
8
|
+
* 设计 (2026-04-19 方案 v2):
|
|
9
|
+
* 首次安装的 hint 文本量不大, 直接**中英并列**. agent 会根据用户发来的消息
|
|
10
|
+
* 语言自动选对应语言回复 — 不需要服务端 detectLang, 更鲁棒
|
|
11
|
+
* (不依赖 env / Intl / locale 判断, 不会踩 macOS LANG=en_US 但系统中文 的坑).
|
|
10
12
|
*
|
|
11
|
-
*
|
|
12
|
-
* docs/prd/PRD-BSO-M9-HARNESS-HOOKS.md (双语 priming 原则延伸到 hint)
|
|
13
|
+
* 每条 hint 尾部明示: [Reply in the user's language | 请用用户所用的语言回复].
|
|
13
14
|
*/
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
15
|
+
const LANG_INSTRUCTION = '[Reply in the user\'s language | 请用用户所用的语言回复用户]';
|
|
16
|
+
// ── activation_success ────────────────────────────────────────────────
|
|
17
|
+
const ACTIVATION_SUCCESS_ZH = [
|
|
18
|
+
'[中文] 激活成功!brain_key 已保存到 ~/.skillfm/config.json。停止轮询。',
|
|
19
|
+
'用温暖的语气告诉用户:「SkillFM 激活成功!🎉 配置已保存至 ~/.skillfm/config.json」',
|
|
20
|
+
'然后立即调用 GET /skills 发现可用技能。',
|
|
21
|
+
'以对话的方式介绍技能列表 — 为每个技能用大白话解释它能做什么, ',
|
|
22
|
+
'并基于上下文推荐一个用户可能想先试的。',
|
|
23
|
+
].join(' ');
|
|
24
|
+
const ACTIVATION_SUCCESS_EN = [
|
|
25
|
+
'[English] Activation successful! brain_key saved to ~/.skillfm/config.json.',
|
|
26
|
+
'Stop polling. Tell the user warmly:',
|
|
27
|
+
'"SkillFM activated! 🎉 Config saved to ~/.skillfm/config.json"',
|
|
28
|
+
'then immediately call GET /skills to discover available skills.',
|
|
29
|
+
'Present the skill list in a friendly, conversational way — for each skill',
|
|
30
|
+
'explain what it can do in plain language, and suggest one the user might',
|
|
31
|
+
'want to try first based on context.',
|
|
32
|
+
].join(' ');
|
|
33
|
+
// ── running_activated ─────────────────────────────────────────────────
|
|
34
|
+
function runningActivatedZh(url) {
|
|
35
|
+
return [
|
|
36
|
+
`[中文] skillfm-local 正在 ${url} 运行。brain key 已加载 — SkillFM 已激活。`,
|
|
37
|
+
'告诉用户「SkillFM 已就绪!」, 然后调用',
|
|
38
|
+
`GET ${url}/skills 发现可用技能。`,
|
|
24
39
|
'以对话的方式介绍 — 用大白话解释每个技能能做什么, 推荐一个可以试试的。',
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const HINTS_EN = {
|
|
33
|
-
activation_success: () => 'Activation successful! brain_key saved to ~/.skillfm/config.json. ' +
|
|
34
|
-
'Stop polling. Tell the user warmly: ' +
|
|
35
|
-
'"SkillFM activated! 🎉 Config saved to ~/.skillfm/config.json" ' +
|
|
36
|
-
'then immediately call GET /skills to discover available skills. ' +
|
|
37
|
-
'Present the skill list in a friendly, conversational way — for each skill ' +
|
|
38
|
-
'explain what it can do in plain language, and suggest one the user might ' +
|
|
39
|
-
'want to try first based on context.',
|
|
40
|
-
running_activated: ({ url = '' }) => `skillfm-local is running at ${url}. Brain key loaded — SkillFM is already activated. ` +
|
|
41
|
-
`Tell the user "SkillFM is ready!" then call GET ${url}/skills to discover ` +
|
|
42
|
-
'available skills. Present them conversationally — explain what each skill does ' +
|
|
40
|
+
].join(' ');
|
|
41
|
+
}
|
|
42
|
+
function runningActivatedEn(url) {
|
|
43
|
+
return [
|
|
44
|
+
`[English] skillfm-local is running at ${url}. Brain key loaded — SkillFM is already activated.`,
|
|
45
|
+
`Tell the user "SkillFM is ready!" then call GET ${url}/skills to discover`,
|
|
46
|
+
'available skills. Present them conversationally — explain what each skill does',
|
|
43
47
|
'in plain language and suggest one to try.',
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
].join(' ');
|
|
49
|
+
}
|
|
50
|
+
// ── running_not_activated ─────────────────────────────────────────────
|
|
51
|
+
function runningNotActivatedZh(url) {
|
|
52
|
+
return [
|
|
53
|
+
`[中文] skillfm-local 正在 ${url} 运行。尚未激活。`,
|
|
54
|
+
`下一步: POST ${url}/activate/start (无请求体) 启动 OAuth 设备流。`,
|
|
55
|
+
'它会返回一个验证链接 — 用友好的语气告诉用户:',
|
|
56
|
+
'「我需要你打开一个链接来完成 SkillFM 激活, 只需要几秒钟:」然后展示链接。',
|
|
57
|
+
`之后每 5 秒轮询 POST ${url}/activate/poll 直到激活。`,
|
|
58
|
+
'不要问用户是否想激活 — 直接做, 这是必需步骤。',
|
|
59
|
+
].join(' ');
|
|
60
|
+
}
|
|
61
|
+
function runningNotActivatedEn(url) {
|
|
62
|
+
return [
|
|
63
|
+
`[English] skillfm-local is running at ${url}. Not yet activated.`,
|
|
64
|
+
`Next step: POST ${url}/activate/start (no body) to start the OAuth device flow.`,
|
|
65
|
+
'It returns a verification URL — tell the user in a friendly way:',
|
|
66
|
+
'"I need you to open a link to complete SkillFM activation, just a few seconds:"',
|
|
67
|
+
`then show the URL. After that, poll POST ${url}/activate/poll every 5 seconds`,
|
|
68
|
+
'until activated. Do NOT ask the user if they want to activate — just do it,',
|
|
50
69
|
'it is required.',
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
en: HINTS_EN,
|
|
55
|
-
};
|
|
70
|
+
].join(' ');
|
|
71
|
+
}
|
|
72
|
+
// ── public API ────────────────────────────────────────────────────────
|
|
56
73
|
/**
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
* @param key hint 类型
|
|
60
|
-
* @param vars 模板变量 (如 url)
|
|
61
|
-
* @param lang 可选, 显式指定语言; 不传则走 detectLang(process.env)
|
|
74
|
+
* 返回指定 key 的双语并列 hint. Agent 按用户消息语言自动匹配回复.
|
|
62
75
|
*/
|
|
63
|
-
export function agentHint(key, vars = {}
|
|
64
|
-
|
|
76
|
+
export function agentHint(key, vars = {}) {
|
|
77
|
+
const url = vars.url ?? '';
|
|
78
|
+
switch (key) {
|
|
79
|
+
case 'activation_success':
|
|
80
|
+
return [ACTIVATION_SUCCESS_ZH, ACTIVATION_SUCCESS_EN, LANG_INSTRUCTION].join('\n\n');
|
|
81
|
+
case 'running_activated':
|
|
82
|
+
return [runningActivatedZh(url), runningActivatedEn(url), LANG_INSTRUCTION].join('\n\n');
|
|
83
|
+
case 'running_not_activated':
|
|
84
|
+
return [runningNotActivatedZh(url), runningNotActivatedEn(url), LANG_INSTRUCTION].join('\n\n');
|
|
85
|
+
}
|
|
65
86
|
}
|
|
66
87
|
//# sourceMappingURL=agent-hints.js.map
|
package/dist/agent-hints.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-hints.js","sourceRoot":"","sources":["../src/agent-hints.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"agent-hints.js","sourceRoot":"","sources":["../src/agent-hints.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAYH,MAAM,gBAAgB,GACpB,iDAAiD,CAAC;AAEpD,yEAAyE;AAEzE,MAAM,qBAAqB,GAAG;IAC5B,uDAAuD;IACvD,4DAA4D;IAC5D,4BAA4B;IAC5B,mCAAmC;IACnC,qBAAqB;CACtB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAEZ,MAAM,qBAAqB,GAAG;IAC5B,6EAA6E;IAC7E,qCAAqC;IACrC,gEAAgE;IAChE,iEAAiE;IACjE,2EAA2E;IAC3E,0EAA0E;IAC1E,qCAAqC;CACtC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAEZ,yEAAyE;AAEzE,SAAS,kBAAkB,CAAC,GAAW;IACrC,OAAO;QACL,yBAAyB,GAAG,kCAAkC;QAC9D,0BAA0B;QAC1B,OAAO,GAAG,iBAAiB;QAC3B,uCAAuC;KACxC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACd,CAAC;AAED,SAAS,kBAAkB,CAAC,GAAW;IACrC,OAAO;QACL,yCAAyC,GAAG,oDAAoD;QAChG,mDAAmD,GAAG,qBAAqB;QAC3E,gFAAgF;QAChF,2CAA2C;KAC5C,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACd,CAAC;AAED,yEAAyE;AAEzE,SAAS,qBAAqB,CAAC,GAAW;IACxC,OAAO;QACL,yBAAyB,GAAG,WAAW;QACvC,aAAa,GAAG,sCAAsC;QACtD,0BAA0B;QAC1B,4CAA4C;QAC5C,kBAAkB,GAAG,sBAAsB;QAC3C,2BAA2B;KAC5B,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACd,CAAC;AAED,SAAS,qBAAqB,CAAC,GAAW;IACxC,OAAO;QACL,yCAAyC,GAAG,sBAAsB;QAClE,mBAAmB,GAAG,2DAA2D;QACjF,kEAAkE;QAClE,iFAAiF;QACjF,4CAA4C,GAAG,gCAAgC;QAC/E,6EAA6E;QAC7E,iBAAiB;KAClB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACd,CAAC;AAED,yEAAyE;AAEzE;;GAEG;AACH,MAAM,UAAU,SAAS,CAAC,GAAiB,EAAE,OAAsB,EAAE;IACnE,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,EAAE,CAAC;IAC3B,QAAQ,GAAG,EAAE,CAAC;QACZ,KAAK,oBAAoB;YACvB,OAAO,CAAC,qBAAqB,EAAE,qBAAqB,EAAE,gBAAgB,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACvF,KAAK,mBAAmB;YACtB,OAAO,CAAC,kBAAkB,CAAC,GAAG,CAAC,EAAE,kBAAkB,CAAC,GAAG,CAAC,EAAE,gBAAgB,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC3F,KAAK,uBAAuB;YAC1B,OAAO,CAAC,qBAAqB,CAAC,GAAG,CAAC,EAAE,qBAAqB,CAAC,GAAG,CAAC,EAAE,gBAAgB,CAAC,CAAC,IAAI,CACpF,MAAM,CACP,CAAC;IACN,CAAC;AACH,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@skillfm/local",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.4",
|
|
4
4
|
"description": "SkillFM local sidecar — a tiny localhost HTTP proxy that lets any AI agent activate and run SkillFM skills in the current conversation without restarting its MCP runtime. Writes ~/.skillfm/local.json for service discovery.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|