@playcraft/cli 0.1.70 → 0.1.71
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 +20 -1
- package/dist/commands/skills.js +68 -4
- package/dist/project-skills/discovery.js +248 -0
- package/dist/project-skills/messages.js +80 -0
- package/dist/project-skills/remote-activation.js +500 -0
- package/dist/project-skills/remote-client.js +117 -0
- package/dist/project-skills/remote-commands.js +87 -0
- package/dist/project-skills/remote-prepare.js +223 -0
- package/dist/project-skills/search-command.js +47 -0
- package/dist/project-skills/search.js +96 -0
- package/dist/recording/project-digest.js +1 -1
- package/dist/workspace-runtime/batch/source-capture.js +1 -1
- package/dist/workspace-runtime/codex/app-server.js +25 -1
- package/dist/workspace-runtime/codex/model-broker.js +93 -6
- package/dist/workspace-runtime/codex/skill-discovery-broker.js +174 -0
- package/dist/workspace-runtime/serve.js +5 -0
- package/dist/workspace-runtime/server/dispatch.js +121 -28
- package/dist/workspace-runtime/server/http.js +14 -0
- package/dist/workspace-runtime/server/operation-record.js +3 -1
- package/dist/workspace-runtime/workspaces/files.js +20 -14
- package/dist/workspace-runtime/workspaces/skill-activation-recovery.js +54 -0
- package/dist/workspace-runtime/workspaces/skill-activation-requests.js +512 -0
- package/dist/workspace-runtime/workspaces/skills.js +13 -176
- package/package.json +3 -3
- package/project-template/.claude/skills/playcraft-skill-recommender/SKILL.md +28 -6
- package/project-template-v2/.claude/skills/playcraft-skill-recommender/SKILL.md +28 -6
- package/project-templates/empty/gitignore +1 -0
- package/project-templates/phaser/.claude/skills/playcraft-skill-recommender/SKILL.md +28 -6
- package/project-templates/threejs/.claude/skills/playcraft-skill-recommender/SKILL.md +28 -6
|
@@ -1,26 +1,17 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
|
|
2
|
+
import { cp, readdir, rm } from 'node:fs/promises';
|
|
3
|
+
import { basename, join } from 'node:path';
|
|
4
|
+
import { canonicalProjectSkillsJson, inspectProjectSkills, readProjectSkillsLockFile } from '@playcraft/common/project-skills';
|
|
5
|
+
import { ensureProjectSkillDirectory as safeDirectory, readLockedProjectSkillFiles as lockedFiles, projectSkillMetadata as metadata, readLockedProjectSkillSnapshot as readWorkspaceSkillSnapshot, freezeLockedProjectSkills, } from '../../project-skills/discovery.js';
|
|
6
|
+
export { readLockedProjectSkillSnapshot as readWorkspaceSkillSnapshot } from '../../project-skills/discovery.js';
|
|
7
7
|
import { initializeLocalProjectSkills } from '../../project-skills/local-store.js';
|
|
8
8
|
import { resolveCliPackageRoot } from '../../scaffold/compose.js';
|
|
9
9
|
import { selectPlatformSkillSnapshot } from '../../project-skills/builtin-import.js';
|
|
10
10
|
import { EMPTY_WORKSPACE_REPOSITORY_SKILLS, EMPTY_WORKSPACE_PLATFORM_SKILLS } from './empty-template.js';
|
|
11
11
|
import { excludeFromGit } from './dependencies.js';
|
|
12
12
|
import { selectOptionalPlatformSkills } from './optional-platform-skills.js';
|
|
13
|
-
import { ensureSharedDirectory
|
|
13
|
+
import { ensureSharedDirectory } from './shared-write.js';
|
|
14
14
|
import { ensureWorkspaceRemixReferenceSkill } from './remix-reference-skill.js';
|
|
15
|
-
/** Runtime availability is determined by the locked metadata, independent of unsynced source edits. */
|
|
16
|
-
export async function readWorkspaceSkillSnapshot(root) {
|
|
17
|
-
const management = await lstat(join(root, '.playcraft'));
|
|
18
|
-
const declaration = join(root, '.playcraft/skills.json');
|
|
19
|
-
const stat = await lstat(declaration);
|
|
20
|
-
if (!management.isDirectory() || management.isSymbolicLink() || !stat.isFile() || stat.isSymbolicLink() || stat.size > 1024 * 1024)
|
|
21
|
-
throw new Error('workspace_skills_invalid');
|
|
22
|
-
return validateProjectSkillsSnapshot(await readFile(declaration, 'utf8'), await readProjectSkillsLockFile(root));
|
|
23
|
-
}
|
|
24
15
|
/** Maintains the bundled reference guide while preserving project selection and custom revisions. */
|
|
25
16
|
export async function ensureWorkspaceSkills(root, cache, access, options = {}) {
|
|
26
17
|
await excludeFromGit(root, '/.playcraft/skills/');
|
|
@@ -61,177 +52,23 @@ export async function ensureWorkspaceSkills(root, cache, access, options = {}) {
|
|
|
61
52
|
},
|
|
62
53
|
});
|
|
63
54
|
}
|
|
64
|
-
async function safeDirectory(root, path) {
|
|
65
|
-
let current = root;
|
|
66
|
-
for (const segment of path.split('/')) {
|
|
67
|
-
current = join(current, segment);
|
|
68
|
-
// Validate every existing segment before creating anything, so a link never redirects the tree.
|
|
69
|
-
const stat = await lstat(current).catch((error) => {
|
|
70
|
-
if (error.code === 'ENOENT')
|
|
71
|
-
return undefined;
|
|
72
|
-
throw error;
|
|
73
|
-
});
|
|
74
|
-
if (stat && (!stat.isDirectory() || stat.isSymbolicLink()))
|
|
75
|
-
throw new Error('workspace_skills_invalid');
|
|
76
|
-
}
|
|
77
|
-
await ensureSharedDirectory(root, current);
|
|
78
|
-
return current;
|
|
79
|
-
}
|
|
80
|
-
/** Cache misses may reconstruct only the exact locked repository bytes, never a newer source. */
|
|
81
|
-
async function lockedFiles(root, cache, entry, view, loadArtifact) {
|
|
82
|
-
const persistent = await safeDirectory(root, '.playcraft/skill-artifacts');
|
|
83
|
-
const saved = join(persistent, `${entry.revision.artifactHash}.tar.gz`);
|
|
84
|
-
const path = join(cache, `${entry.revision.artifactHash}.tar.gz`);
|
|
85
|
-
const contentHash = view.lock.lockVersion === 4 ? entry.revision.contentHash : undefined;
|
|
86
|
-
const contentSaved = contentHash ? join(persistent, `content-${contentHash}.tar.gz`) : undefined;
|
|
87
|
-
const contentPath = contentHash ? join(cache, `content-${contentHash}.tar.gz`) : undefined;
|
|
88
|
-
let archive;
|
|
89
|
-
let contentAddressed = false;
|
|
90
|
-
try {
|
|
91
|
-
const stat = await lstat(saved);
|
|
92
|
-
if (!stat.isFile() || stat.isSymbolicLink() || stat.nlink !== 1 || stat.size > 80 * 1024 * 1024)
|
|
93
|
-
throw new Error('workspace_skills_invalid_bundle');
|
|
94
|
-
archive = await readFile(saved);
|
|
95
|
-
}
|
|
96
|
-
catch (error) {
|
|
97
|
-
if (error.code !== 'ENOENT')
|
|
98
|
-
throw error;
|
|
99
|
-
try {
|
|
100
|
-
if (!contentSaved)
|
|
101
|
-
throw error;
|
|
102
|
-
const stat = await lstat(contentSaved);
|
|
103
|
-
if (!stat.isFile() || stat.isSymbolicLink() || stat.nlink !== 1 || stat.size > 80 * 1024 * 1024)
|
|
104
|
-
throw new Error('workspace_skills_invalid_bundle');
|
|
105
|
-
archive = await readFile(contentSaved);
|
|
106
|
-
contentAddressed = true;
|
|
107
|
-
}
|
|
108
|
-
catch (cacheError) {
|
|
109
|
-
if (cacheError.code !== 'ENOENT')
|
|
110
|
-
throw cacheError;
|
|
111
|
-
try {
|
|
112
|
-
archive = await readFile(path);
|
|
113
|
-
}
|
|
114
|
-
catch (error) {
|
|
115
|
-
if (error.code !== 'ENOENT')
|
|
116
|
-
throw error;
|
|
117
|
-
try {
|
|
118
|
-
if (!contentPath)
|
|
119
|
-
throw error;
|
|
120
|
-
archive = await readFile(contentPath);
|
|
121
|
-
contentAddressed = true;
|
|
122
|
-
}
|
|
123
|
-
catch (contentError) {
|
|
124
|
-
if (contentError.code !== 'ENOENT')
|
|
125
|
-
throw contentError;
|
|
126
|
-
const declaration = view.declaration.entries.find(item => item.entryKey === entry.entryKey);
|
|
127
|
-
if (entry.revision.kind === 'source' && declaration.source.kind === 'repository') {
|
|
128
|
-
const source = await readProjectSkillSource(root, declaration);
|
|
129
|
-
if (source.sourceTreeHash !== entry.sourceTreeHash)
|
|
130
|
-
throw new Error('workspace_skills_source_changed');
|
|
131
|
-
if (contentHash) {
|
|
132
|
-
if (source.sourceTreeHash !== contentHash)
|
|
133
|
-
throw new Error('workspace_skills_digest_mismatch');
|
|
134
|
-
return source.files.map(file => ({ path: file.path, data: Buffer.from(file.content) }));
|
|
135
|
-
}
|
|
136
|
-
archive = createCanonicalBundleArchive(source.files.map(file => ({ path: file.path, data: Buffer.from(file.content) })));
|
|
137
|
-
}
|
|
138
|
-
else {
|
|
139
|
-
if (!loadArtifact)
|
|
140
|
-
throw new Error('workspace_skills_artifact_unavailable');
|
|
141
|
-
archive = await loadArtifact(entry);
|
|
142
|
-
contentAddressed = Boolean(contentHash);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
if (!contentAddressed && createHash('sha256').update(archive).digest('hex') !== entry.revision.artifactHash)
|
|
149
|
-
throw new Error('workspace_skills_digest_mismatch');
|
|
150
|
-
const extracted = extractBundleArchive(archive);
|
|
151
|
-
if (extracted.issues.length)
|
|
152
|
-
throw new Error('workspace_skills_invalid_bundle');
|
|
153
|
-
if (contentHash && sourceTreeDigest(extracted.files.map(file => ({ path: file.path, content: file.data }))) !== contentHash)
|
|
154
|
-
throw new Error('workspace_skills_digest_mismatch');
|
|
155
|
-
if (entry.revision.kind === 'source' && sourceTreeDigest(extracted.files.map(file => ({ path: file.path, content: file.data }))) !== entry.sourceTreeHash)
|
|
156
|
-
throw new Error('workspace_skills_digest_mismatch');
|
|
157
|
-
await writeSharedFile(root, contentAddressed ? contentSaved : saved, archive);
|
|
158
|
-
return extracted.files;
|
|
159
|
-
}
|
|
160
|
-
function metadata(entryKey, content) {
|
|
161
|
-
const frontmatter = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/.exec(content);
|
|
162
|
-
const parsed = frontmatter ? load(frontmatter[1]) : {};
|
|
163
|
-
const data = parsed && typeof parsed === 'object' ? parsed : {};
|
|
164
|
-
return { entryKey, name: typeof data.name === 'string' ? data.name : entryKey,
|
|
165
|
-
description: typeof data.description === 'string' ? data.description : '', content };
|
|
166
|
-
}
|
|
167
55
|
export async function readWorkspaceSkills(root, cache, loadArtifact) {
|
|
168
56
|
const snapshot = await readWorkspaceSkillSnapshot(root);
|
|
169
|
-
const
|
|
57
|
+
const reads = await Promise.allSettled(snapshot.lock.entries.map(async (entry) => {
|
|
170
58
|
const files = await lockedFiles(root, cache, entry, snapshot, loadArtifact);
|
|
171
59
|
const body = files.find(file => file.path === 'SKILL.md');
|
|
172
60
|
if (!body)
|
|
173
61
|
throw new Error('workspace_skills_body_missing');
|
|
174
62
|
return metadata(entry.entryKey, body.data.toString('utf8'));
|
|
175
63
|
}));
|
|
64
|
+
const skills = reads.map(result => {
|
|
65
|
+
if (result.status === 'rejected')
|
|
66
|
+
throw result.reason;
|
|
67
|
+
return result.value;
|
|
68
|
+
});
|
|
176
69
|
return { ...snapshot, lockHash: createHash('sha256').update(canonicalProjectSkillsJson(snapshot.lock)).digest('hex'), skills };
|
|
177
70
|
}
|
|
178
71
|
/** Read-only task generation lives outside source discovery and is omitted from Git/snapshots. */
|
|
179
72
|
export async function freezeWorkspaceSkills(root, cache, loadArtifact) {
|
|
180
|
-
|
|
181
|
-
const entries = effectiveProjectSkills(snapshot.declaration, snapshot.lock, 'sandbox-agent');
|
|
182
|
-
const base = await safeDirectory(root, '.playcraft/skills');
|
|
183
|
-
const lockHash = createHash('sha256').update(canonicalProjectSkillsJson(snapshot.lock)).digest('hex');
|
|
184
|
-
const generation = await safeDirectory(base, lockHash);
|
|
185
|
-
const skills = [];
|
|
186
|
-
const expectedFiles = new Set();
|
|
187
|
-
for (const entry of entries) {
|
|
188
|
-
const files = await lockedFiles(root, cache, entry, snapshot, loadArtifact);
|
|
189
|
-
for (const file of files) {
|
|
190
|
-
const target = join(generation, entry.entryKey, file.path);
|
|
191
|
-
const relative = join(entry.entryKey, file.path);
|
|
192
|
-
expectedFiles.add(relative);
|
|
193
|
-
await safeDirectory(generation, dirname(relative));
|
|
194
|
-
// shared
|
|
195
|
-
await writeFile(target, file.data, { flag: 'wx', mode: 0o444 }).catch(error => {
|
|
196
|
-
if (error.code !== 'EEXIST')
|
|
197
|
-
throw error;
|
|
198
|
-
});
|
|
199
|
-
const handle = await open(target, constants.O_RDONLY | constants.O_NOFOLLOW);
|
|
200
|
-
try {
|
|
201
|
-
const stat = await handle.stat();
|
|
202
|
-
if (!stat.isFile() || stat.nlink !== 1 || !file.data.equals(await handle.readFile()))
|
|
203
|
-
throw new Error('workspace_skills_digest_mismatch');
|
|
204
|
-
// A private controller umask must not remove the developer's read access.
|
|
205
|
-
// Repair only the verified inode, keeping every frozen byte read-only.
|
|
206
|
-
await handle.chmod(0o444);
|
|
207
|
-
}
|
|
208
|
-
finally {
|
|
209
|
-
await handle.close();
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
const body = files.find(file => file.path === 'SKILL.md');
|
|
213
|
-
if (!body)
|
|
214
|
-
throw new Error('workspace_skills_body_missing');
|
|
215
|
-
const info = metadata(entry.entryKey, body.data.toString('utf8'));
|
|
216
|
-
skills.push({ name: info.name, description: info.description, skillRef: entry.revision.skillRef,
|
|
217
|
-
revision: entry.revision.kind === 'source' ? entry.revision.sourceRevision : entry.revision.version,
|
|
218
|
-
path: join(generation, entry.entryKey, 'SKILL.md') });
|
|
219
|
-
}
|
|
220
|
-
const verifyTree = async (directory, relative = '') => {
|
|
221
|
-
for (const item of await readdir(directory, { withFileTypes: true })) {
|
|
222
|
-
const path = join(relative, item.name);
|
|
223
|
-
if (item.isSymbolicLink())
|
|
224
|
-
throw new Error('workspace_skills_invalid_bundle');
|
|
225
|
-
if (item.isDirectory()) {
|
|
226
|
-
if (![...expectedFiles].some(file => file.startsWith(`${path}/`)))
|
|
227
|
-
throw new Error('workspace_skills_invalid_bundle');
|
|
228
|
-
await verifyTree(join(directory, item.name), path);
|
|
229
|
-
}
|
|
230
|
-
else if (!item.isFile() || !expectedFiles.has(path))
|
|
231
|
-
throw new Error('workspace_skills_invalid_bundle');
|
|
232
|
-
}
|
|
233
|
-
};
|
|
234
|
-
await verifyTree(generation);
|
|
235
|
-
return { declarationHash: snapshot.lock.declarationHash,
|
|
236
|
-
lockHash, skills };
|
|
73
|
+
return freezeLockedProjectSkills(root, cache, loadArtifact);
|
|
237
74
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@playcraft/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.71",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -56,8 +56,8 @@
|
|
|
56
56
|
"@gltf-transform/core": "^4.3.0",
|
|
57
57
|
"@gltf-transform/extensions": "^4.3.0",
|
|
58
58
|
"@gltf-transform/functions": "^4.3.0",
|
|
59
|
-
"@playcraft/build": "0.1.
|
|
60
|
-
"@playcraft/common": "0.1.
|
|
59
|
+
"@playcraft/build": "0.1.71",
|
|
60
|
+
"@playcraft/common": "0.1.71",
|
|
61
61
|
"better-sqlite3-pinned": "npm:better-sqlite3@13.0.3",
|
|
62
62
|
"chokidar": "^4.0.3",
|
|
63
63
|
"commander": "^13.1.0",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: playcraft-skill-recommender
|
|
3
|
-
description:
|
|
3
|
+
description: 开发前从远端技能库发现候选,按意图、引擎、触发词和能力匹配;选定精确版本后下载技能及依赖。普通技能与 Atom 均可发现,不依赖本地安装目录。已锁定技能仍由当前执行锁约束。
|
|
4
4
|
triggers: 构建新游戏项目, 选择 skill, 查找可用组件, 3D项目搭建, Three.js项目, Phaser项目
|
|
5
5
|
compatibility: agent,opencode
|
|
6
6
|
---
|
|
@@ -13,6 +13,28 @@ compatibility: agent,opencode
|
|
|
13
13
|
|
|
14
14
|
---
|
|
15
15
|
|
|
16
|
+
## 默认流程:远端发现 → 精确选择 → 按需下载
|
|
17
|
+
|
|
18
|
+
`skills match` 默认查询当前身份有权访问的远端技能元数据,不依赖本地技能目录,也不会下载所有正文:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
playcraft skills match --intent "military,merge,base-building,strategy,island,battle" --engine threejs --json > /tmp/skills-match.json
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
候选包含 `skillRef`、`skillVersionId`、`version`、`artifactHash`、名称、描述、引擎、能力与匹配词。依据任务选择候选,再提交原精确引用,不能重新取最新版:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
playcraft skills prepare --from-match /tmp/skills-match.json --skills "<匹配结果中的namespace/slug>" --json
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
- 选择阶段复验权限、引擎和硬依赖,生成独立选择锁;准备阶段只下载该锁的精确制品,校验后存入 `.playcraft/skill-selections/<selectionId>/`,相同选择可复用已校验缓存。
|
|
31
|
+
- 在云端工程会话中,`prepare` 下载校验成功后默认返回 `activation_requested` / `executionReady: false`。请正常结束本回合,不要执行准备副本或重复申请同一接续;平台等待当前回合及其他原生执行结束后,在同一会话的新回合激活精确技能、读取新锁正文并继续原任务,保留已有修改。当前回合的锁始终不变。
|
|
32
|
+
- `already_active` 表示这些精确技能已在当前锁中启用且满足本回合的执行要求,不会重复启动新回合;使用当前锁中的冻结路径。需要独占执行的技能若尚未获得本回合授权,仍会安排独占后续回合。需要只下载时使用 `--prepare-only`。本地无云端执行代理时仍返回 `prepared_requires_activation`,不会自行启动对话。
|
|
33
|
+
- 激活保留原有技能及版本,遇到同引用不同修订、用户中途改锁、工程改绑定或权限撤回会明确失败,不会换最新版重试。来源回合取消、失败、结果未知或已有新的用户回合时,不自动重放原任务。
|
|
34
|
+
- 当前可执行技能可用 `playcraft skills search --query "<关键词>" --json` 或 `playcraft skills match --installed --intent "<关键词>" --json` 查询,只返回当前锁内已启用的技能。普通技能无需 Atom manifest,使用前读取返回的冻结 `path`。
|
|
35
|
+
- 远端失败须报告真实失败,不能退回目录扫描,也不能把失败当作没有匹配。沙箱自动复用当前执行身份;本地使用已登录的平台环境。不要索取或输出控制凭据。
|
|
36
|
+
- 只有确实在本地编写 Atom 库时,才显式使用 `match --local` 或 `--skills-dir`。下文旧目录工具仅适用于这一场景,不能用于绕过运行时技能锁。
|
|
37
|
+
|
|
16
38
|
## 按场景选择命令
|
|
17
39
|
|
|
18
40
|
| 你的场景 | 推荐命令 | 说明 |
|
|
@@ -33,10 +55,10 @@ compatibility: agent,opencode
|
|
|
33
55
|
|
|
34
56
|
```bash
|
|
35
57
|
# 自动推断引擎
|
|
36
|
-
playcraft skills match --intent "<关键词,逗号分隔>" --json
|
|
58
|
+
playcraft skills match --local --intent "<关键词,逗号分隔>" --json
|
|
37
59
|
|
|
38
60
|
# 指定引擎
|
|
39
|
-
playcraft skills match --engine <threejs|phaser> --intent "<关键词>" --json
|
|
61
|
+
playcraft skills match --local --engine <threejs|phaser> --intent "<关键词>" --json
|
|
40
62
|
```
|
|
41
63
|
|
|
42
64
|
**适用时机**:你已经知道要做什么类型的游戏,需要一次性获取完整的技术栈推荐。
|
|
@@ -51,13 +73,13 @@ playcraft skills match --engine <threejs|phaser> --intent "<关键词>" --json
|
|
|
51
73
|
|
|
52
74
|
```bash
|
|
53
75
|
# 三消游戏
|
|
54
|
-
playcraft skills match --intent "match3,grid,tile,eliminate" --json
|
|
76
|
+
playcraft skills match --local --intent "match3,grid,tile,eliminate" --json
|
|
55
77
|
|
|
56
78
|
# 麻将收集消除
|
|
57
|
-
playcraft skills match --intent "mahjong,tile,match,stacking,puzzle" --json
|
|
79
|
+
playcraft skills match --local --intent "mahjong,tile,match,stacking,puzzle" --json
|
|
58
80
|
|
|
59
81
|
# Three.js 3D 棋盘
|
|
60
|
-
playcraft skills match --engine threejs --intent "board,3d,path,animation" --json
|
|
82
|
+
playcraft skills match --local --engine threejs --intent "board,3d,path,animation" --json
|
|
61
83
|
```
|
|
62
84
|
|
|
63
85
|
---
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: playcraft-skill-recommender
|
|
3
|
-
description:
|
|
3
|
+
description: 开发前从远端技能库发现候选,按意图、引擎、触发词和能力匹配;选定精确版本后下载技能及依赖。普通技能与 Atom 均可发现,不依赖本地安装目录。已锁定技能仍由当前执行锁约束。
|
|
4
4
|
triggers: 构建新游戏项目, 选择 skill, 查找可用组件, 3D项目搭建, Three.js项目, Phaser项目
|
|
5
5
|
compatibility: agent,opencode
|
|
6
6
|
---
|
|
@@ -13,6 +13,28 @@ compatibility: agent,opencode
|
|
|
13
13
|
|
|
14
14
|
---
|
|
15
15
|
|
|
16
|
+
## 默认流程:远端发现 → 精确选择 → 按需下载
|
|
17
|
+
|
|
18
|
+
`skills match` 默认查询当前身份有权访问的远端技能元数据,不依赖本地技能目录,也不会下载所有正文:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
playcraft skills match --intent "military,merge,base-building,strategy,island,battle" --engine threejs --json > /tmp/skills-match.json
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
候选包含 `skillRef`、`skillVersionId`、`version`、`artifactHash`、名称、描述、引擎、能力与匹配词。依据任务选择候选,再提交原精确引用,不能重新取最新版:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
playcraft skills prepare --from-match /tmp/skills-match.json --skills "<匹配结果中的namespace/slug>" --json
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
- 选择阶段复验权限、引擎和硬依赖,生成独立选择锁;准备阶段只下载该锁的精确制品,校验后存入 `.playcraft/skill-selections/<selectionId>/`,相同选择可复用已校验缓存。
|
|
31
|
+
- 在云端工程会话中,`prepare` 下载校验成功后默认返回 `activation_requested` / `executionReady: false`。请正常结束本回合,不要执行准备副本或重复申请同一接续;平台等待当前回合及其他原生执行结束后,在同一会话的新回合激活精确技能、读取新锁正文并继续原任务,保留已有修改。当前回合的锁始终不变。
|
|
32
|
+
- `already_active` 表示这些精确技能已在当前锁中启用且满足本回合的执行要求,不会重复启动新回合;使用当前锁中的冻结路径。需要独占执行的技能若尚未获得本回合授权,仍会安排独占后续回合。需要只下载时使用 `--prepare-only`。本地无云端执行代理时仍返回 `prepared_requires_activation`,不会自行启动对话。
|
|
33
|
+
- 激活保留原有技能及版本,遇到同引用不同修订、用户中途改锁、工程改绑定或权限撤回会明确失败,不会换最新版重试。来源回合取消、失败、结果未知或已有新的用户回合时,不自动重放原任务。
|
|
34
|
+
- 当前可执行技能可用 `playcraft skills search --query "<关键词>" --json` 或 `playcraft skills match --installed --intent "<关键词>" --json` 查询,只返回当前锁内已启用的技能。普通技能无需 Atom manifest,使用前读取返回的冻结 `path`。
|
|
35
|
+
- 远端失败须报告真实失败,不能退回目录扫描,也不能把失败当作没有匹配。沙箱自动复用当前执行身份;本地使用已登录的平台环境。不要索取或输出控制凭据。
|
|
36
|
+
- 只有确实在本地编写 Atom 库时,才显式使用 `match --local` 或 `--skills-dir`。下文旧目录工具仅适用于这一场景,不能用于绕过运行时技能锁。
|
|
37
|
+
|
|
16
38
|
## 按场景选择命令
|
|
17
39
|
|
|
18
40
|
| 你的场景 | 推荐命令 | 说明 |
|
|
@@ -35,10 +57,10 @@ compatibility: agent,opencode
|
|
|
35
57
|
|
|
36
58
|
```bash
|
|
37
59
|
# 自动推断引擎
|
|
38
|
-
playcraft skills match --intent "<关键词,逗号分隔>" --json
|
|
60
|
+
playcraft skills match --local --intent "<关键词,逗号分隔>" --json
|
|
39
61
|
|
|
40
62
|
# 指定引擎
|
|
41
|
-
playcraft skills match --engine <threejs|phaser> --intent "<关键词>" --json
|
|
63
|
+
playcraft skills match --local --engine <threejs|phaser> --intent "<关键词>" --json
|
|
42
64
|
```
|
|
43
65
|
|
|
44
66
|
**适用时机**:你已经知道要做什么类型的游戏,需要一次性获取完整的技术栈推荐。
|
|
@@ -53,13 +75,13 @@ playcraft skills match --engine <threejs|phaser> --intent "<关键词>" --json
|
|
|
53
75
|
|
|
54
76
|
```bash
|
|
55
77
|
# 三消游戏
|
|
56
|
-
playcraft skills match --intent "match3,grid,tile,eliminate" --json
|
|
78
|
+
playcraft skills match --local --intent "match3,grid,tile,eliminate" --json
|
|
57
79
|
|
|
58
80
|
# 麻将收集消除
|
|
59
|
-
playcraft skills match --intent "mahjong,tile,match,stacking,puzzle" --json
|
|
81
|
+
playcraft skills match --local --intent "mahjong,tile,match,stacking,puzzle" --json
|
|
60
82
|
|
|
61
83
|
# Three.js 3D 棋盘
|
|
62
|
-
playcraft skills match --engine threejs --intent "board,3d,path,animation" --json
|
|
84
|
+
playcraft skills match --local --engine threejs --intent "board,3d,path,animation" --json
|
|
63
85
|
```
|
|
64
86
|
|
|
65
87
|
---
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: playcraft-skill-recommender
|
|
3
|
-
description:
|
|
3
|
+
description: 开发前从远端技能库发现候选,按意图、引擎、触发词和能力匹配;选定精确版本后下载技能及依赖。普通技能与 Atom 均可发现,不依赖本地安装目录。已锁定技能仍由当前执行锁约束。
|
|
4
4
|
triggers: 构建新游戏项目, 选择 skill, 查找可用组件, 3D项目搭建, Three.js项目, Phaser项目
|
|
5
5
|
compatibility: agent,opencode
|
|
6
6
|
---
|
|
@@ -13,6 +13,28 @@ compatibility: agent,opencode
|
|
|
13
13
|
|
|
14
14
|
---
|
|
15
15
|
|
|
16
|
+
## 默认流程:远端发现 → 精确选择 → 按需下载
|
|
17
|
+
|
|
18
|
+
`skills match` 默认查询当前身份有权访问的远端技能元数据,不依赖本地技能目录,也不会下载所有正文:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
playcraft skills match --intent "military,merge,base-building,strategy,island,battle" --engine threejs --json > /tmp/skills-match.json
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
候选包含 `skillRef`、`skillVersionId`、`version`、`artifactHash`、名称、描述、引擎、能力与匹配词。依据任务选择候选,再提交原精确引用,不能重新取最新版:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
playcraft skills prepare --from-match /tmp/skills-match.json --skills "<匹配结果中的namespace/slug>" --json
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
- 选择阶段复验权限、引擎和硬依赖,生成独立选择锁;准备阶段只下载该锁的精确制品,校验后存入 `.playcraft/skill-selections/<selectionId>/`,相同选择可复用已校验缓存。
|
|
31
|
+
- 在云端工程会话中,`prepare` 下载校验成功后默认返回 `activation_requested` / `executionReady: false`。请正常结束本回合,不要执行准备副本或重复申请同一接续;平台等待当前回合及其他原生执行结束后,在同一会话的新回合激活精确技能、读取新锁正文并继续原任务,保留已有修改。当前回合的锁始终不变。
|
|
32
|
+
- `already_active` 表示这些精确技能已在当前锁中启用且满足本回合的执行要求,不会重复启动新回合;使用当前锁中的冻结路径。需要独占执行的技能若尚未获得本回合授权,仍会安排独占后续回合。需要只下载时使用 `--prepare-only`。本地无云端执行代理时仍返回 `prepared_requires_activation`,不会自行启动对话。
|
|
33
|
+
- 激活保留原有技能及版本,遇到同引用不同修订、用户中途改锁、工程改绑定或权限撤回会明确失败,不会换最新版重试。来源回合取消、失败、结果未知或已有新的用户回合时,不自动重放原任务。
|
|
34
|
+
- 当前可执行技能可用 `playcraft skills search --query "<关键词>" --json` 或 `playcraft skills match --installed --intent "<关键词>" --json` 查询,只返回当前锁内已启用的技能。普通技能无需 Atom manifest,使用前读取返回的冻结 `path`。
|
|
35
|
+
- 远端失败须报告真实失败,不能退回目录扫描,也不能把失败当作没有匹配。沙箱自动复用当前执行身份;本地使用已登录的平台环境。不要索取或输出控制凭据。
|
|
36
|
+
- 只有确实在本地编写 Atom 库时,才显式使用 `match --local` 或 `--skills-dir`。下文旧目录工具仅适用于这一场景,不能用于绕过运行时技能锁。
|
|
37
|
+
|
|
16
38
|
## 按场景选择命令
|
|
17
39
|
|
|
18
40
|
| 你的场景 | 推荐命令 | 说明 |
|
|
@@ -33,10 +55,10 @@ compatibility: agent,opencode
|
|
|
33
55
|
|
|
34
56
|
```bash
|
|
35
57
|
# 自动推断引擎
|
|
36
|
-
playcraft skills match --intent "<关键词,逗号分隔>" --json
|
|
58
|
+
playcraft skills match --local --intent "<关键词,逗号分隔>" --json
|
|
37
59
|
|
|
38
60
|
# 指定引擎
|
|
39
|
-
playcraft skills match --engine <threejs|phaser> --intent "<关键词>" --json
|
|
61
|
+
playcraft skills match --local --engine <threejs|phaser> --intent "<关键词>" --json
|
|
40
62
|
```
|
|
41
63
|
|
|
42
64
|
**适用时机**:你已经知道要做什么类型的游戏,需要一次性获取完整的技术栈推荐。
|
|
@@ -51,13 +73,13 @@ playcraft skills match --engine <threejs|phaser> --intent "<关键词>" --json
|
|
|
51
73
|
|
|
52
74
|
```bash
|
|
53
75
|
# 三消游戏
|
|
54
|
-
playcraft skills match --intent "match3,grid,tile,eliminate" --json
|
|
76
|
+
playcraft skills match --local --intent "match3,grid,tile,eliminate" --json
|
|
55
77
|
|
|
56
78
|
# 麻将收集消除
|
|
57
|
-
playcraft skills match --intent "mahjong,tile,match,stacking,puzzle" --json
|
|
79
|
+
playcraft skills match --local --intent "mahjong,tile,match,stacking,puzzle" --json
|
|
58
80
|
|
|
59
81
|
# Three.js 3D 棋盘
|
|
60
|
-
playcraft skills match --engine threejs --intent "board,3d,path,animation" --json
|
|
82
|
+
playcraft skills match --local --engine threejs --intent "board,3d,path,animation" --json
|
|
61
83
|
```
|
|
62
84
|
|
|
63
85
|
---
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: playcraft-skill-recommender
|
|
3
|
-
description:
|
|
3
|
+
description: 开发前从远端技能库发现候选,按意图、引擎、触发词和能力匹配;选定精确版本后下载技能及依赖。普通技能与 Atom 均可发现,不依赖本地安装目录。已锁定技能仍由当前执行锁约束。
|
|
4
4
|
triggers: 构建新游戏项目, 选择 skill, 查找可用组件, 3D项目搭建, Three.js项目, Phaser项目
|
|
5
5
|
compatibility: agent,opencode
|
|
6
6
|
---
|
|
@@ -13,6 +13,28 @@ compatibility: agent,opencode
|
|
|
13
13
|
|
|
14
14
|
---
|
|
15
15
|
|
|
16
|
+
## 默认流程:远端发现 → 精确选择 → 按需下载
|
|
17
|
+
|
|
18
|
+
`skills match` 默认查询当前身份有权访问的远端技能元数据,不依赖本地技能目录,也不会下载所有正文:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
playcraft skills match --intent "military,merge,base-building,strategy,island,battle" --engine threejs --json > /tmp/skills-match.json
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
候选包含 `skillRef`、`skillVersionId`、`version`、`artifactHash`、名称、描述、引擎、能力与匹配词。依据任务选择候选,再提交原精确引用,不能重新取最新版:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
playcraft skills prepare --from-match /tmp/skills-match.json --skills "<匹配结果中的namespace/slug>" --json
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
- 选择阶段复验权限、引擎和硬依赖,生成独立选择锁;准备阶段只下载该锁的精确制品,校验后存入 `.playcraft/skill-selections/<selectionId>/`,相同选择可复用已校验缓存。
|
|
31
|
+
- 在云端工程会话中,`prepare` 下载校验成功后默认返回 `activation_requested` / `executionReady: false`。请正常结束本回合,不要执行准备副本或重复申请同一接续;平台等待当前回合及其他原生执行结束后,在同一会话的新回合激活精确技能、读取新锁正文并继续原任务,保留已有修改。当前回合的锁始终不变。
|
|
32
|
+
- `already_active` 表示这些精确技能已在当前锁中启用且满足本回合的执行要求,不会重复启动新回合;使用当前锁中的冻结路径。需要独占执行的技能若尚未获得本回合授权,仍会安排独占后续回合。需要只下载时使用 `--prepare-only`。本地无云端执行代理时仍返回 `prepared_requires_activation`,不会自行启动对话。
|
|
33
|
+
- 激活保留原有技能及版本,遇到同引用不同修订、用户中途改锁、工程改绑定或权限撤回会明确失败,不会换最新版重试。来源回合取消、失败、结果未知或已有新的用户回合时,不自动重放原任务。
|
|
34
|
+
- 当前可执行技能可用 `playcraft skills search --query "<关键词>" --json` 或 `playcraft skills match --installed --intent "<关键词>" --json` 查询,只返回当前锁内已启用的技能。普通技能无需 Atom manifest,使用前读取返回的冻结 `path`。
|
|
35
|
+
- 远端失败须报告真实失败,不能退回目录扫描,也不能把失败当作没有匹配。沙箱自动复用当前执行身份;本地使用已登录的平台环境。不要索取或输出控制凭据。
|
|
36
|
+
- 只有确实在本地编写 Atom 库时,才显式使用 `match --local` 或 `--skills-dir`。下文旧目录工具仅适用于这一场景,不能用于绕过运行时技能锁。
|
|
37
|
+
|
|
16
38
|
## 按场景选择命令
|
|
17
39
|
|
|
18
40
|
| 你的场景 | 推荐命令 | 说明 |
|
|
@@ -33,10 +55,10 @@ compatibility: agent,opencode
|
|
|
33
55
|
|
|
34
56
|
```bash
|
|
35
57
|
# 自动推断引擎
|
|
36
|
-
playcraft skills match --intent "<关键词,逗号分隔>" --json
|
|
58
|
+
playcraft skills match --local --intent "<关键词,逗号分隔>" --json
|
|
37
59
|
|
|
38
60
|
# 指定引擎
|
|
39
|
-
playcraft skills match --engine <threejs|phaser> --intent "<关键词>" --json
|
|
61
|
+
playcraft skills match --local --engine <threejs|phaser> --intent "<关键词>" --json
|
|
40
62
|
```
|
|
41
63
|
|
|
42
64
|
**适用时机**:你已经知道要做什么类型的游戏,需要一次性获取完整的技术栈推荐。
|
|
@@ -51,13 +73,13 @@ playcraft skills match --engine <threejs|phaser> --intent "<关键词>" --json
|
|
|
51
73
|
|
|
52
74
|
```bash
|
|
53
75
|
# 三消游戏
|
|
54
|
-
playcraft skills match --intent "match3,grid,tile,eliminate" --json
|
|
76
|
+
playcraft skills match --local --intent "match3,grid,tile,eliminate" --json
|
|
55
77
|
|
|
56
78
|
# 麻将收集消除
|
|
57
|
-
playcraft skills match --intent "mahjong,tile,match,stacking,puzzle" --json
|
|
79
|
+
playcraft skills match --local --intent "mahjong,tile,match,stacking,puzzle" --json
|
|
58
80
|
|
|
59
81
|
# Three.js 3D 棋盘
|
|
60
|
-
playcraft skills match --engine threejs --intent "board,3d,path,animation" --json
|
|
82
|
+
playcraft skills match --local --engine threejs --intent "board,3d,path,animation" --json
|
|
61
83
|
```
|
|
62
84
|
|
|
63
85
|
---
|