@lythos/skill-deck 0.1.2 → 0.1.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/package.json +1 -1
- package/src/cli.ts +4 -2
- package/src/link.ts +35 -10
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -3,13 +3,15 @@ import { linkDeck } from './link.js'
|
|
|
3
3
|
|
|
4
4
|
const command = process.argv[2]
|
|
5
5
|
const deckFlagIdx = process.argv.indexOf('--deck')
|
|
6
|
+
const workdirFlagIdx = process.argv.indexOf('--workdir')
|
|
6
7
|
const deckPath = deckFlagIdx >= 0 ? process.argv[deckFlagIdx + 1] : undefined
|
|
8
|
+
const workdir = workdirFlagIdx >= 0 ? process.argv[workdirFlagIdx + 1] : undefined
|
|
7
9
|
|
|
8
10
|
switch (command) {
|
|
9
11
|
case 'link':
|
|
10
|
-
linkDeck(deckPath)
|
|
12
|
+
linkDeck(deckPath, workdir)
|
|
11
13
|
break
|
|
12
14
|
default:
|
|
13
|
-
console.error('Usage: lythoskill-deck link [--deck <path>]')
|
|
15
|
+
console.error('Usage: lythoskill-deck link [--deck <path>] [--workdir <dir>]')
|
|
14
16
|
process.exit(1)
|
|
15
17
|
}
|
package/src/link.ts
CHANGED
|
@@ -83,6 +83,26 @@ function extractArrayField(fm: string, field: string): string[] {
|
|
|
83
83
|
// ── 冷池查找 ────────────────────────────────────────────────
|
|
84
84
|
|
|
85
85
|
function findSource(name: string, coldPool: string, projectDir: string): string | null {
|
|
86
|
+
// 0. Fully-qualified path: host.tld/owner/repo/skill
|
|
87
|
+
// → cold_pool/host.tld/owner/repo/skills/skill
|
|
88
|
+
// Also handles host.tld/owner/repo (standalone skill without skills/ subdir)
|
|
89
|
+
const fqMatch = name.match(/^[a-z0-9-]+\.[a-z0-9-]+\//);
|
|
90
|
+
if (fqMatch) {
|
|
91
|
+
const parts = name.split("/");
|
|
92
|
+
const host = parts[0]; // github.com
|
|
93
|
+
const owner = parts[1]; // lythos-labs
|
|
94
|
+
const repo = parts[2]; // lythoskill
|
|
95
|
+
const skill = parts.slice(3).join("/"); // lythoskill-deck
|
|
96
|
+
|
|
97
|
+
if (skill) {
|
|
98
|
+
const fqPath = join(coldPool, host, owner, repo, "skills", skill);
|
|
99
|
+
if (existsSync(join(fqPath, "SKILL.md"))) return fqPath;
|
|
100
|
+
}
|
|
101
|
+
// fallback: standalone skill at repo root
|
|
102
|
+
const directPath = join(coldPool, host, owner, repo);
|
|
103
|
+
if (existsSync(join(directPath, "SKILL.md"))) return directPath;
|
|
104
|
+
}
|
|
105
|
+
|
|
86
106
|
// 1. 直接路径
|
|
87
107
|
const direct = resolve(coldPool, name);
|
|
88
108
|
if (existsSync(join(direct, "SKILL.md"))) return direct;
|
|
@@ -94,10 +114,18 @@ function findSource(name: string, coldPool: string, projectDir: string): string
|
|
|
94
114
|
if (existsSync(join(mono, "SKILL.md"))) return mono;
|
|
95
115
|
}
|
|
96
116
|
|
|
97
|
-
// 3.
|
|
117
|
+
// 3. 项目本地: <project>/skills/<name>(build 输出目录,优先级高于扁平扫描)
|
|
118
|
+
const local = resolve(projectDir, "skills", name);
|
|
119
|
+
if (existsSync(join(local, "SKILL.md"))) return local;
|
|
120
|
+
|
|
121
|
+
// 4. 扁平扫描: cold_pool/<any-repo>/<name> 或 <any-repo>/skills/<name>
|
|
122
|
+
// 跳过隐藏目录(agent working set、git、配置等)和 node_modules,
|
|
123
|
+
// 避免把 .claude/skills/ 里的 symlink 误判为有效 cold-pool 源
|
|
98
124
|
try {
|
|
99
125
|
for (const entry of readdirSync(coldPool, { withFileTypes: true })) {
|
|
100
126
|
if (!entry.isDirectory()) continue;
|
|
127
|
+
if (entry.name.startsWith('.')) continue;
|
|
128
|
+
if (entry.name === 'node_modules') continue;
|
|
101
129
|
const base = join(coldPool, entry.name);
|
|
102
130
|
for (const sub of [join(base, name), join(base, "skills", name)]) {
|
|
103
131
|
if (existsSync(join(sub, "SKILL.md"))) return sub;
|
|
@@ -105,16 +133,12 @@ function findSource(name: string, coldPool: string, projectDir: string): string
|
|
|
105
133
|
}
|
|
106
134
|
} catch {}
|
|
107
135
|
|
|
108
|
-
// 4. 项目本地: <project>/skills/<name>
|
|
109
|
-
const local = resolve(projectDir, "skills", name);
|
|
110
|
-
if (existsSync(join(local, "SKILL.md"))) return local;
|
|
111
|
-
|
|
112
136
|
return null;
|
|
113
137
|
}
|
|
114
138
|
|
|
115
139
|
// ── 主流程 ──────────────────────────────────────────────────
|
|
116
140
|
|
|
117
|
-
export function linkDeck(cliDeckPath?: string): void {
|
|
141
|
+
export function linkDeck(cliDeckPath?: string, cliWorkdir?: string): void {
|
|
118
142
|
const cliDeck = cliDeckPath || process.argv.find((_, i, a) => a[i - 1] === "--deck");
|
|
119
143
|
const DECK_PATH = cliDeck
|
|
120
144
|
? resolve(cliDeck)
|
|
@@ -133,7 +157,7 @@ if (!existsSync(DECK_PATH)) {
|
|
|
133
157
|
process.exit(1);
|
|
134
158
|
}
|
|
135
159
|
|
|
136
|
-
const PROJECT_DIR = dirname(DECK_PATH);
|
|
160
|
+
const PROJECT_DIR = cliWorkdir ? resolve(cliWorkdir) : dirname(DECK_PATH);
|
|
137
161
|
const deckRaw = readFileSync(DECK_PATH, "utf-8");
|
|
138
162
|
const deckHash = hashContent(deckRaw);
|
|
139
163
|
const deck = parseToml(deckRaw) as any;
|
|
@@ -213,10 +237,11 @@ const linkedSkills: LinkedSkill[] = [];
|
|
|
213
237
|
for (const item of declared) {
|
|
214
238
|
const dest = join(WORKING_SET, item.name);
|
|
215
239
|
|
|
216
|
-
//
|
|
217
|
-
|
|
240
|
+
// 幂等:已存在则删除重建(lstat 不跟随 symlink,能处理断链/自引用 symlink)
|
|
241
|
+
try {
|
|
242
|
+
lstatSync(dest);
|
|
218
243
|
rmSync(dest, { recursive: true, force: true });
|
|
219
|
-
}
|
|
244
|
+
} catch {}
|
|
220
245
|
|
|
221
246
|
try {
|
|
222
247
|
mkdirSync(dirname(dest), { recursive: true });
|