skill-family-engineering-kit 0.19.2 → 0.20.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.
- package/CHANGELOG.md +35 -0
- package/CHANGELOG.zh-CN.md +35 -0
- package/README.md +20 -15
- package/README.zh-CN.md +20 -15
- package/data/hosts/cursor/host-descriptor.json +34 -0
- package/data/hosts/registry.json +1 -0
- package/docs/en/quickstart/index.html +5 -4
- package/docs/integration/audit/version-compatibility/index.html +6 -3
- package/docs/public/status/index.html +3 -3
- package/docs/quickstart/index.html +4 -3
- package/docs/search/search_index.json +1 -1
- package/package.json +3 -3
- package/release-notes/0.19.3.yaml +15 -0
- package/release-notes/0.20.0.yaml +21 -0
- package/src/host-verification-drivers.mjs +48 -4
- package/src/host-verification.mjs +29 -19
package/package.json
CHANGED
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
"url": "https://github.com/ifoohoo/skill-family-engineering-kit/issues"
|
|
9
9
|
},
|
|
10
10
|
"dependencies": {
|
|
11
|
-
"skill-family-contracts": "0.
|
|
12
|
-
"skill-family-harness-node": "0.
|
|
11
|
+
"skill-family-contracts": "0.20.0",
|
|
12
|
+
"skill-family-harness-node": "0.20.0"
|
|
13
13
|
},
|
|
14
14
|
"description": "Build-time scaffold, adoption planning, projection, and checks.",
|
|
15
15
|
"engines": {
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"url": "https://github.com/ifoohoo/skill-family-engineering-kit.git"
|
|
52
52
|
},
|
|
53
53
|
"type": "module",
|
|
54
|
-
"version": "0.
|
|
54
|
+
"version": "0.20.0",
|
|
55
55
|
"scripts": {
|
|
56
56
|
"check": "node --test",
|
|
57
57
|
"test": "node --test"
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
version: 0.19.3
|
|
2
|
+
date: 2026-09-08
|
|
3
|
+
locales:
|
|
4
|
+
en:
|
|
5
|
+
summary: Engineering Kit 0.19.3 aligns its package, release documentation, and generated projections with the Foundation Adoption Review Qoder distribution patch. Public commands and behavior are unchanged.
|
|
6
|
+
changes:
|
|
7
|
+
changed:
|
|
8
|
+
- Aligns the package version, release documentation, and verified public package projection with Foundation 0.19.3 without adding a command, capability, or adapter path.
|
|
9
|
+
upgradeNotes: Pin all three Foundation packages to exactly 0.19.3 and rebuild official Bundles under the existing source-identity and integrity rules. The capability catalog, host descriptors, drivers, since values, and maturity are unchanged.
|
|
10
|
+
zh-CN:
|
|
11
|
+
summary: Engineering Kit 0.19.3 将包版本、发布文档和生成投影与 Foundation Adoption Review 的 Qoder 分发补丁对齐。公开命令和行为保持不变。
|
|
12
|
+
changes:
|
|
13
|
+
changed:
|
|
14
|
+
- 包版本、发布文档和经验证的公开 package.json 投影与 Foundation 0.19.3 数值对齐,不增加命令、能力或 adapter 路径。
|
|
15
|
+
upgradeNotes: 三个 Foundation 包须一起精确锁定到 0.19.3,并按既有来源身份与完整性规则重建官方 Bundle。能力目录、宿主 descriptor、driver、since 字段和成熟度均不变。
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
version: 0.20.0
|
|
2
|
+
date: 2026-09-10
|
|
3
|
+
locales:
|
|
4
|
+
en:
|
|
5
|
+
summary: Engineering Kit 0.20.0 adds Cursor project Skill projection and a fixed Cursor Agent verification driver on the existing host-verification path.
|
|
6
|
+
changes:
|
|
7
|
+
added:
|
|
8
|
+
- Registers `.cursor/skills` as the Cursor project Skill root while preserving manual lifecycle planning.
|
|
9
|
+
- Adds `cursor-agent-print-v1` with fixed workspace, ask-mode, sandbox, JSON output, version, and failure checks.
|
|
10
|
+
changed:
|
|
11
|
+
- Qualifies project-level Skill discovery through Cursor CLI menu discovery and one explicit invocation, without requiring a duplicate IDE check.
|
|
12
|
+
upgradeNotes: Pin all three Foundation packages to exactly 0.20.0 and rebuild official Bundles. Cursor stays manual/developer-preview; the driver does not add automatic trust, model selection, or Marketplace lifecycle management.
|
|
13
|
+
zh-CN:
|
|
14
|
+
summary: Engineering Kit 0.20.0 在既有宿主验证路径上增加 Cursor 项目级 Skill 投影和固定 Cursor Agent 验证驱动。
|
|
15
|
+
changes:
|
|
16
|
+
added:
|
|
17
|
+
- 登记 `.cursor/skills` 作为 Cursor 项目级 Skill 根,生命周期计划继续保持手动。
|
|
18
|
+
- 增加 `cursor-agent-print-v1`,固定工作区、问答模式、沙箱、JSON 输出、版本与失败检查。
|
|
19
|
+
changed:
|
|
20
|
+
- 通过 Cursor CLI 菜单发现和一次显式调用取得项目级 Skill 发现资格,不重复执行 IDE 图形界面检查。
|
|
21
|
+
upgradeNotes: 三个 Foundation 包须一起精确锁定到 0.20.0,并重建官方 Bundle。Cursor 继续保持 manual/developer-preview;驱动不增加自动信任、模型选择或 Marketplace 生命周期管理。
|
|
@@ -66,7 +66,7 @@ function workBuddyNativeLifecyclePlan({ request, roots, prompt }) {
|
|
|
66
66
|
nativeStage("preflight", [{ step: "version-probe", args: ["--version"] }]),
|
|
67
67
|
nativeStage("validate-v1", [manage("plugin-validate", "validate", roots.sourceRoot)]),
|
|
68
68
|
nativeStage("marketplace-add", [manage("marketplace-add", "marketplace", "add", roots.sourceRoot, "--name", request.source.marketplaceId)]),
|
|
69
|
-
nativeStage("install-v1", [manage("plugin-install", "install", plugin, "--scope", "local"
|
|
69
|
+
nativeStage("install-v1", [manage("plugin-install", "install", plugin, "--scope", "local")]),
|
|
70
70
|
nativeStage("discover-v1", [manage("plugin-list", "list", "--json")]),
|
|
71
71
|
nativeStage("invoke-v1", [invoke("plugin-invoke")]),
|
|
72
72
|
nativeStage("disable", [
|
|
@@ -324,6 +324,27 @@ export const QODER_DRIVER = Object.freeze({
|
|
|
324
324
|
versionPattern: /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/u,
|
|
325
325
|
});
|
|
326
326
|
|
|
327
|
+
export const CURSOR_DRIVER = Object.freeze({
|
|
328
|
+
hostId: "cursor",
|
|
329
|
+
driverId: "cursor-agent-print-v1",
|
|
330
|
+
driverVersion: "1.0.0",
|
|
331
|
+
executableBasename: "cursor-agent",
|
|
332
|
+
probeArgs: Object.freeze(["--version"]),
|
|
333
|
+
// Frozen invocation (S-047): `cursor-agent --print --output-format json
|
|
334
|
+
// --mode ask --sandbox enabled --workspace <workspace> <prompt>`. The
|
|
335
|
+
// workspace is derived from `<workspace>/.cursor/skills/<skill-id>` and is
|
|
336
|
+
// also the process cwd. Never add force, trust, model, history, worktree,
|
|
337
|
+
// remote-session or MCP auto-approval flags.
|
|
338
|
+
promptFlag: "--print",
|
|
339
|
+
outputArgs: Object.freeze(["--output-format", "json", "--mode", "ask", "--sandbox", "enabled"]),
|
|
340
|
+
cwdFlag: "--workspace",
|
|
341
|
+
promptTrailing: true,
|
|
342
|
+
textProtocol: true,
|
|
343
|
+
// Cursor currently reports a date plus a seven-character lowercase commit
|
|
344
|
+
// abbreviation, for example `2026.09.08-6caf4ff`.
|
|
345
|
+
versionPattern: /^[0-9]{4}\.(?:0[1-9]|1[0-2])\.(?:0[1-9]|[12][0-9]|3[01])-[0-9a-f]{7}$/u,
|
|
346
|
+
});
|
|
347
|
+
|
|
327
348
|
export const BUILT_IN_HOST_VERIFICATION_DRIVERS = Object.freeze({
|
|
328
349
|
[KIMI_DRIVER.driverId]: KIMI_DRIVER,
|
|
329
350
|
[WORKBUDDY_DRIVER.driverId]: WORKBUDDY_DRIVER,
|
|
@@ -331,6 +352,7 @@ export const BUILT_IN_HOST_VERIFICATION_DRIVERS = Object.freeze({
|
|
|
331
352
|
[CLAUDE_DRIVER.driverId]: CLAUDE_DRIVER,
|
|
332
353
|
[CODEX_DRIVER.driverId]: CODEX_DRIVER,
|
|
333
354
|
[QODER_DRIVER.driverId]: QODER_DRIVER,
|
|
355
|
+
[CURSOR_DRIVER.driverId]: CURSOR_DRIVER,
|
|
334
356
|
});
|
|
335
357
|
|
|
336
358
|
export function getBuiltInHostVerificationDriver(driverId) {
|
|
@@ -354,7 +376,7 @@ export function getBuiltInHostVerificationDriver(driverId) {
|
|
|
354
376
|
* - CodeBuddy points HOME at the existing state root only; its project skill
|
|
355
377
|
* discovery is the fresh cwd `.codebuddy/skills` tree and therefore does
|
|
356
378
|
* not set CODEBUDDY_CONFIG_DIR.
|
|
357
|
-
* - claude, codex and
|
|
379
|
+
* - claude, codex, qoder and cursor point HOME at the existing state root only
|
|
358
380
|
* (their login state lives under HOME); no model override, --config-dir or
|
|
359
381
|
* CODEX_HOME override is ever projected.
|
|
360
382
|
*/
|
|
@@ -376,8 +398,8 @@ export function driverEnvironment({ driver, sessionRoot, existingUserStateRoot,
|
|
|
376
398
|
}
|
|
377
399
|
|
|
378
400
|
/**
|
|
379
|
-
* Frozen stream-protocol evaluation, one per admitted driver.
|
|
380
|
-
*
|
|
401
|
+
* Frozen stream-protocol evaluation, one per admitted driver. Text-protocol
|
|
402
|
+
* drivers share the minimal "exit status + frozen structure" shape
|
|
381
403
|
* (R-04/R-05/R-06): the raw stream text is read from the private evidence
|
|
382
404
|
* only and never enters the public result. The product never judges any
|
|
383
405
|
* fixed output string: domain output semantics belong to the consumer and
|
|
@@ -398,6 +420,9 @@ export function driverEnvironment({ driver, sessionRoot, existingUserStateRoot,
|
|
|
398
420
|
* `is_error`; `is_error: true` (including the frozen quota-exhausted
|
|
399
421
|
* error_code 118) is a determined failure that maps to the existing
|
|
400
422
|
* failed + execution-failed, with no account state entering the result.
|
|
423
|
+
* - cursor: `--output-format json` output must be one object with
|
|
424
|
+
* `type=result`, `subtype=success`, `is_error=false` and a string `result`;
|
|
425
|
+
* extra fields are allowed and the answer text is never judged.
|
|
401
426
|
*/
|
|
402
427
|
export function evaluateDriverStreamProtocol({ driver, stdoutText, exitOk }) {
|
|
403
428
|
if (driver.driverId === CLAUDE_DRIVER.driverId) {
|
|
@@ -409,6 +434,9 @@ export function evaluateDriverStreamProtocol({ driver, stdoutText, exitOk }) {
|
|
|
409
434
|
if (driver.driverId === QODER_DRIVER.driverId) {
|
|
410
435
|
return exitOk && qoderJsonOk(stdoutText);
|
|
411
436
|
}
|
|
437
|
+
if (driver.driverId === CURSOR_DRIVER.driverId) {
|
|
438
|
+
return exitOk && cursorJsonOk(stdoutText);
|
|
439
|
+
}
|
|
412
440
|
return exitOk;
|
|
413
441
|
}
|
|
414
442
|
|
|
@@ -485,3 +513,19 @@ function qoderJsonOk(stdoutText) {
|
|
|
485
513
|
if (typeof parsed.is_error !== "boolean") return false;
|
|
486
514
|
return !parsed.is_error;
|
|
487
515
|
}
|
|
516
|
+
|
|
517
|
+
function cursorJsonOk(stdoutText) {
|
|
518
|
+
let parsed;
|
|
519
|
+
try {
|
|
520
|
+
parsed = JSON.parse(stdoutText);
|
|
521
|
+
} catch {
|
|
522
|
+
return false;
|
|
523
|
+
}
|
|
524
|
+
return typeof parsed === "object"
|
|
525
|
+
&& parsed !== null
|
|
526
|
+
&& !Array.isArray(parsed)
|
|
527
|
+
&& parsed.type === "result"
|
|
528
|
+
&& parsed.subtype === "success"
|
|
529
|
+
&& parsed.is_error === false
|
|
530
|
+
&& typeof parsed.result === "string";
|
|
531
|
+
}
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
CLAUDE_DRIVER,
|
|
15
15
|
CODEBUDDY_DRIVER,
|
|
16
16
|
CODEX_DRIVER,
|
|
17
|
+
CURSOR_DRIVER,
|
|
17
18
|
driverEnvironment,
|
|
18
19
|
evaluateDriverStreamProtocol,
|
|
19
20
|
getBuiltInHostVerificationDriver,
|
|
@@ -219,9 +220,10 @@ function assertRootIsolation({ inputRoots, writeRoots, existingUserStateRoot })
|
|
|
219
220
|
* must be a real (non-symlink) directory. The WorkBuddy config root is a
|
|
220
221
|
* fresh write root under the same one-way state rule.
|
|
221
222
|
*
|
|
222
|
-
* Codex and
|
|
223
|
-
* parent must be `<workspace>/.codex/skills
|
|
224
|
-
*
|
|
223
|
+
* Codex, Qoder and Cursor freeze their discovery layout structurally: the
|
|
224
|
+
* installed parent must be `<workspace>/.codex/skills`,
|
|
225
|
+
* `<fresh-ws>/.qoder/skills` or `<workspace>/.cursor/skills` (basenames
|
|
226
|
+
* `skills` and `.codex` / `.qoder` / `.cursor`), so the discovery target can
|
|
225
227
|
* never escape the fresh workspace or reach a real user skill root.
|
|
226
228
|
*
|
|
227
229
|
* Claude freezes the classic plugin layout structurally: the installed
|
|
@@ -274,6 +276,10 @@ async function assertDiscoveryLayout({ driver, installedRoot, existingUserStateR
|
|
|
274
276
|
if (path.basename(skillsRoot) !== "skills") throw fail("Qoder skills directory must be named skills");
|
|
275
277
|
if (path.basename(path.dirname(skillsRoot)) !== ".qoder") throw fail("Qoder discovery root must be named .qoder");
|
|
276
278
|
}
|
|
279
|
+
if (driver.driverId === CURSOR_DRIVER.driverId) {
|
|
280
|
+
if (path.basename(skillsRoot) !== "skills") throw fail("Cursor skills directory must be named skills");
|
|
281
|
+
if (path.basename(path.dirname(skillsRoot)) !== ".cursor") throw fail("Cursor discovery root must be named .cursor");
|
|
282
|
+
}
|
|
277
283
|
if (driver.driverId === CLAUDE_DRIVER.driverId) {
|
|
278
284
|
// Classic Claude plugin layout `<plugin-root>/skills/<skill-id>/SKILL.md`:
|
|
279
285
|
// the skills directory basename is pinned so the --plugin-dir target
|
|
@@ -299,7 +305,9 @@ async function assertDiscoveryLayout({ driver, installedRoot, existingUserStateR
|
|
|
299
305
|
* (`promptTrailing`) so the vector matches the frozen argv byte-for-byte:
|
|
300
306
|
* claude `-p --verbose --no-session-persistence --output-format stream-json
|
|
301
307
|
* <prompt> --plugin-dir <dir>`, codex `exec --json --ephemeral <prompt>`,
|
|
302
|
-
* qoder `-p -o json --no-session-persistence --cwd <fresh-ws> <prompt
|
|
308
|
+
* qoder `-p -o json --no-session-persistence --cwd <fresh-ws> <prompt>`,
|
|
309
|
+
* cursor `--print --output-format json --mode ask --sandbox enabled
|
|
310
|
+
* --workspace <workspace> <prompt>`
|
|
303
311
|
* where the --cwd target is the fresh workspace two segments above the
|
|
304
312
|
* installed parent (`<fresh-ws>/.qoder/skills/<skill-id>`). The claude
|
|
305
313
|
* `--plugin-dir` target is the plugin root one segment above the installed
|
|
@@ -484,16 +492,16 @@ export async function runHostVerification({ request, bindings, hostsRoot } = {})
|
|
|
484
492
|
const installedRoot = requiredBinding(bindings.installedSkillRoot, "installedSkillRoot");
|
|
485
493
|
// The install target is a fresh write root: the same central isolation
|
|
486
494
|
// check covers it against every protected input root, the three other
|
|
487
|
-
// fresh write roots, and the one-way existing user state rule. Codex
|
|
488
|
-
//
|
|
489
|
-
//
|
|
495
|
+
// fresh write roots, and the one-way existing user state rule. Codex and
|
|
496
|
+
// Cursor freeze their workspace-local skill roots: the installed target
|
|
497
|
+
// legitimately sits
|
|
490
498
|
// inside the candidate workspace, which itself sits inside the caller's
|
|
491
|
-
// repository, and its exact position is pinned by the
|
|
499
|
+
// repository, and its exact position is pinned by the driver branches below
|
|
492
500
|
// (`dirname x3(installedRoot) === workspaceRoot`), so workspaceRoot and
|
|
493
501
|
// repositoryRoot are not compared in this call; every other protected
|
|
494
502
|
// input root stays bidirectionally disjoint.
|
|
495
503
|
assertRootIsolation({
|
|
496
|
-
inputRoots:
|
|
504
|
+
inputRoots: [CODEX_DRIVER.driverId, CURSOR_DRIVER.driverId].includes(driver.driverId)
|
|
497
505
|
? inputRoots.filter(([name]) => name !== "workspaceRoot" && name !== "repositoryRoot")
|
|
498
506
|
: inputRoots,
|
|
499
507
|
writeRoots: [...baseWriteRoots, ["installedSkillRoot", installedRoot]],
|
|
@@ -539,12 +547,14 @@ export async function runHostVerification({ request, bindings, hostsRoot } = {})
|
|
|
539
547
|
if (gitInfo === null) return rejected();
|
|
540
548
|
if (path.dirname(path.dirname(path.dirname(installedRoot))) !== workspaceRoot) return rejected();
|
|
541
549
|
}
|
|
550
|
+
if (driver.driverId === CURSOR_DRIVER.driverId
|
|
551
|
+
&& path.dirname(path.dirname(path.dirname(installedRoot))) !== workspaceRoot) return rejected();
|
|
542
552
|
if (await stat(installedRoot).then(() => true, () => false)) return rejected();
|
|
543
553
|
const adapterMembers = requiredMembers(bindings, "adapterMembers");
|
|
544
|
-
if (
|
|
545
|
-
//
|
|
546
|
-
//
|
|
547
|
-
//
|
|
554
|
+
if ([QODER_DRIVER.driverId, CURSOR_DRIVER.driverId].includes(driver.driverId) && !adapterMembers.includes("SKILL.md")) {
|
|
555
|
+
// A missing SKILL.md in the adapter table is a statically decidable
|
|
556
|
+
// preflight failure for these sole project-level projections — reject
|
|
557
|
+
// before materialization with zero side effects.
|
|
548
558
|
return rejected();
|
|
549
559
|
}
|
|
550
560
|
if (driver.driverId === CODEX_DRIVER.driverId) {
|
|
@@ -595,10 +605,10 @@ export async function runHostVerification({ request, bindings, hostsRoot } = {})
|
|
|
595
605
|
existingUserStateRoot,
|
|
596
606
|
installedParent: path.dirname(installedRoot),
|
|
597
607
|
});
|
|
598
|
-
//
|
|
599
|
-
//
|
|
600
|
-
//
|
|
601
|
-
const processCwd =
|
|
608
|
+
// Codex and Cursor must start in the candidate workspace selected by
|
|
609
|
+
// their frozen invocation vectors; all other drivers keep their existing
|
|
610
|
+
// cwd rules.
|
|
611
|
+
const processCwd = [CODEX_DRIVER.driverId, CURSOR_DRIVER.driverId].includes(driver.driverId)
|
|
602
612
|
? workspaceRoot
|
|
603
613
|
: driver.driverId === CODEBUDDY_DRIVER.driverId
|
|
604
614
|
? discovery.projectRoot
|
|
@@ -734,8 +744,8 @@ export async function runHostVerification({ request, bindings, hostsRoot } = {})
|
|
|
734
744
|
if (cause?.code !== "SFC2004") throw cause;
|
|
735
745
|
return finishDeterminedProcess({ status: "failed", reason: "private-evidence-publication-failed", extra: { execution: publicExecution(invocation), snapshots } });
|
|
736
746
|
}
|
|
737
|
-
// Frozen output-protocol judgement.
|
|
738
|
-
//
|
|
747
|
+
// Frozen output-protocol judgement. Drivers marked textProtocol decode
|
|
748
|
+
// the invocation stdout once more from the
|
|
739
749
|
// private evidence (digest-bound) as UTF-8 text and judge the closed
|
|
740
750
|
// driver protocol; the raw text itself never enters the public result.
|
|
741
751
|
// Kimi and WorkBuddy keep their 0.11.0 raw-byte behavior: no decode, no
|