@lythos/skill-deck 0.1.1 → 0.1.3
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 +45 -19
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
|
@@ -23,14 +23,8 @@ import {
|
|
|
23
23
|
// ── 路径工具 ────────────────────────────────────────────────
|
|
24
24
|
|
|
25
25
|
function findDeckToml(from: string): string | null {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const p = join(dir, "skill-deck.toml");
|
|
29
|
-
if (existsSync(p)) return p;
|
|
30
|
-
const parent = dirname(dir);
|
|
31
|
-
if (parent === dir) break;
|
|
32
|
-
dir = parent;
|
|
33
|
-
}
|
|
26
|
+
const p = join(from, "skill-deck.toml");
|
|
27
|
+
if (existsSync(p)) return p;
|
|
34
28
|
return null;
|
|
35
29
|
}
|
|
36
30
|
|
|
@@ -89,6 +83,26 @@ function extractArrayField(fm: string, field: string): string[] {
|
|
|
89
83
|
// ── 冷池查找 ────────────────────────────────────────────────
|
|
90
84
|
|
|
91
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
|
+
|
|
92
106
|
// 1. 直接路径
|
|
93
107
|
const direct = resolve(coldPool, name);
|
|
94
108
|
if (existsSync(join(direct, "SKILL.md"))) return direct;
|
|
@@ -100,10 +114,17 @@ function findSource(name: string, coldPool: string, projectDir: string): string
|
|
|
100
114
|
if (existsSync(join(mono, "SKILL.md"))) return mono;
|
|
101
115
|
}
|
|
102
116
|
|
|
103
|
-
// 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 相关目录,避免把 symlink 误判为有效源
|
|
123
|
+
const skipDirs = new Set(['.claude', '.kimi', '.git', 'node_modules', '.lythos-curator']);
|
|
104
124
|
try {
|
|
105
125
|
for (const entry of readdirSync(coldPool, { withFileTypes: true })) {
|
|
106
126
|
if (!entry.isDirectory()) continue;
|
|
127
|
+
if (skipDirs.has(entry.name)) continue;
|
|
107
128
|
const base = join(coldPool, entry.name);
|
|
108
129
|
for (const sub of [join(base, name), join(base, "skills", name)]) {
|
|
109
130
|
if (existsSync(join(sub, "SKILL.md"))) return sub;
|
|
@@ -111,27 +132,31 @@ function findSource(name: string, coldPool: string, projectDir: string): string
|
|
|
111
132
|
}
|
|
112
133
|
} catch {}
|
|
113
134
|
|
|
114
|
-
// 4. 项目本地: <project>/skills/<name>
|
|
115
|
-
const local = resolve(projectDir, "skills", name);
|
|
116
|
-
if (existsSync(join(local, "SKILL.md"))) return local;
|
|
117
|
-
|
|
118
135
|
return null;
|
|
119
136
|
}
|
|
120
137
|
|
|
121
138
|
// ── 主流程 ──────────────────────────────────────────────────
|
|
122
139
|
|
|
123
|
-
export function linkDeck(cliDeckPath?: string): void {
|
|
140
|
+
export function linkDeck(cliDeckPath?: string, cliWorkdir?: string): void {
|
|
124
141
|
const cliDeck = cliDeckPath || process.argv.find((_, i, a) => a[i - 1] === "--deck");
|
|
125
142
|
const DECK_PATH = cliDeck
|
|
126
143
|
? resolve(cliDeck)
|
|
127
144
|
: findDeckToml(process.cwd()) || resolve("skill-deck.toml");
|
|
128
145
|
|
|
129
146
|
if (!existsSync(DECK_PATH)) {
|
|
130
|
-
console.error(`❌ ${
|
|
147
|
+
console.error(`❌ skill-deck.toml not found in ${process.cwd()}`);
|
|
148
|
+
console.error(`\nCreate one:`);
|
|
149
|
+
console.error(` cat > skill-deck.toml <<'EOF'`);
|
|
150
|
+
console.error(` [deck]`);
|
|
151
|
+
console.error(` max_cards = 10`);
|
|
152
|
+
console.error(` \n [tool]`);
|
|
153
|
+
console.error(` skills = ["lythoskill-deck"]`);
|
|
154
|
+
console.error(` EOF`);
|
|
155
|
+
console.error(`\nOr specify a path: bunx @lythos/skill-deck link --deck /path/to/deck.toml`);
|
|
131
156
|
process.exit(1);
|
|
132
157
|
}
|
|
133
158
|
|
|
134
|
-
const PROJECT_DIR = dirname(DECK_PATH);
|
|
159
|
+
const PROJECT_DIR = cliWorkdir ? resolve(cliWorkdir) : dirname(DECK_PATH);
|
|
135
160
|
const deckRaw = readFileSync(DECK_PATH, "utf-8");
|
|
136
161
|
const deckHash = hashContent(deckRaw);
|
|
137
162
|
const deck = parseToml(deckRaw) as any;
|
|
@@ -211,10 +236,11 @@ const linkedSkills: LinkedSkill[] = [];
|
|
|
211
236
|
for (const item of declared) {
|
|
212
237
|
const dest = join(WORKING_SET, item.name);
|
|
213
238
|
|
|
214
|
-
//
|
|
215
|
-
|
|
239
|
+
// 幂等:已存在则删除重建(lstat 不跟随 symlink,能处理断链/自引用 symlink)
|
|
240
|
+
try {
|
|
241
|
+
lstatSync(dest);
|
|
216
242
|
rmSync(dest, { recursive: true, force: true });
|
|
217
|
-
}
|
|
243
|
+
} catch {}
|
|
218
244
|
|
|
219
245
|
try {
|
|
220
246
|
mkdirSync(dirname(dest), { recursive: true });
|