@lingxia/skill 0.8.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/README.md +95 -0
- package/bin/install.mjs +247 -0
- package/package.json +49 -0
- package/scripts/sync.mjs +69 -0
- package/skill/SKILL.md +334 -0
- package/skill/app/apple-sdk.md +312 -0
- package/skill/app/applinks.md +289 -0
- package/skill/app/project.md +760 -0
- package/skill/cli/lxdev.md +195 -0
- package/skill/cli/reference.md +481 -0
- package/skill/examples/hello-host-js/README.md +25 -0
- package/skill/examples/hello-host-js/home/lxapp.json +12 -0
- package/skill/examples/hello-host-js/home/pages/home/index.json +4 -0
- package/skill/examples/hello-host-js/home/pages/home/index.ts +14 -0
- package/skill/examples/hello-host-js/home/pages/home/index.tsx +15 -0
- package/skill/examples/hello-host-js/lingxia.yaml +39 -0
- package/skill/examples/hello-host-rust/Cargo.toml +15 -0
- package/skill/examples/hello-host-rust/README.md +44 -0
- package/skill/examples/hello-host-rust/home/lxapp.json +13 -0
- package/skill/examples/hello-host-rust/home/pages/home/index.html +46 -0
- package/skill/examples/hello-host-rust/home/pages/home/index.json +4 -0
- package/skill/examples/hello-host-rust/lingxia.yaml +32 -0
- package/skill/examples/hello-host-rust/src/lib.rs +58 -0
- package/skill/examples/hello-lxapp/README.md +29 -0
- package/skill/examples/hello-lxapp/lxapp.config.ts +8 -0
- package/skill/examples/hello-lxapp/lxapp.json +14 -0
- package/skill/examples/hello-lxapp/package.json +14 -0
- package/skill/examples/hello-lxapp/pages/home/index.json +4 -0
- package/skill/examples/hello-lxapp/pages/home/index.ts +35 -0
- package/skill/examples/hello-lxapp/pages/home/index.tsx +34 -0
- package/skill/lxapp/bridge.md +654 -0
- package/skill/lxapp/components.md +375 -0
- package/skill/lxapp/guide.md +675 -0
- package/skill/lxapp/lx-api.md +481 -0
- package/skill/native/development.md +414 -0
- package/skill/reference/file-lifecycle.md +325 -0
- package/skill/skill-manifest.json +6 -0
package/README.md
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# @lingxia/skill
|
|
2
|
+
|
|
3
|
+
The development skill for the [LingXia](https://github.com/LingXia-Dev/LingXia) cross-platform app framework, as a plain-markdown bundle you can install into any project.
|
|
4
|
+
|
|
5
|
+
Open it with any AI coding tool that reads project files — Claude Code, the Claude Agent SDK, OpenAI Codex CLI, Cursor — and it will route the agent through LingXia's decision tree, recipes, CLI / component / native-API references as you build:
|
|
6
|
+
|
|
7
|
+
- **standalone lxapps** — page-based mini-apps with a View + Logic split (React, Vue, or HTML view; JS Logic)
|
|
8
|
+
- **native host apps** — Android, iOS, macOS, and Harmony shells embedding an lxapp
|
|
9
|
+
- **Rust native extensions** — `#[lingxia::native]` routes and `lingxia::js` AppService extensions
|
|
10
|
+
|
|
11
|
+
The on-disk layout follows the [Anthropic Skills convention](https://docs.claude.com/en/docs/claude-code/skills) — a `SKILL.md` entry with a frontmatter manifest plus relative sub-files. Tools that don't use that convention can still read the content directly; see _Codex CLI and other tools_ below.
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
The skill is opt-in — `lingxia new` doesn't bundle it, only prints a hint pointing at the install command below. Install it once per project (or globally per user):
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# Project-scoped: ./.claude/skills/lingxia/
|
|
19
|
+
npx @lingxia/skill install
|
|
20
|
+
|
|
21
|
+
# User-scoped: ~/.claude/skills/lingxia/
|
|
22
|
+
npx @lingxia/skill install --user
|
|
23
|
+
|
|
24
|
+
# Custom location: <path>/lingxia/
|
|
25
|
+
npx @lingxia/skill install --target ./agent
|
|
26
|
+
|
|
27
|
+
# Preview without writing
|
|
28
|
+
npx @lingxia/skill install --dry-run
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Then open the project in your AI tool of choice. Claude tools discover `.claude/skills/` directly. Codex and many other agents should be given an `AGENTS.md` pointer; see the next section.
|
|
32
|
+
|
|
33
|
+
### Codex CLI and other tools
|
|
34
|
+
|
|
35
|
+
OpenAI Codex CLI reads repository instructions from `AGENTS.md`; it does not rely on Claude's `.claude/skills/` discovery path. Pass `--agents-md` and the installer writes the skill normally, then adds a small pointer block to `<project>/AGENTS.md` directing Codex at the installed `SKILL.md`:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npx @lingxia/skill install --agents-md
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The block is fenced with HTML markers, so re-running the installer replaces it in place rather than appending duplicates. If the file doesn't exist yet, it's created; if it already has content, the pointer is appended.
|
|
42
|
+
|
|
43
|
+
For Cursor, GitHub Copilot, or any agent that reads project markdown on demand, the default install plus a short rule in `.cursorrules` / `.github/copilot-instructions.md` pointing at `.claude/skills/lingxia/SKILL.md` works fine.
|
|
44
|
+
|
|
45
|
+
### Other commands
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
npx @lingxia/skill where # print the target install path
|
|
49
|
+
npx @lingxia/skill uninstall # remove the installed skill directory
|
|
50
|
+
npx @lingxia/skill version # print the skill version
|
|
51
|
+
npx @lingxia/skill --help
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
(`uninstall` leaves `AGENTS.md` untouched — humans may have added content to it. Remove the LingXia block manually if you want it gone.)
|
|
55
|
+
|
|
56
|
+
## Versioning
|
|
57
|
+
|
|
58
|
+
`@lingxia/skill` ships with the same version number as the `lingxia` CLI and the rest of `@lingxia/*`. Pin a specific version when reproducibility matters:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
npx @lingxia/skill@0.7.0 install
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
The skill's content documents the CLI surface; CLI releases that change a flag bump the skill version in lockstep.
|
|
65
|
+
|
|
66
|
+
## What's inside
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
.claude/skills/lingxia/
|
|
70
|
+
├── SKILL.md # entry: decision tree, fast-path recipes, symptom router
|
|
71
|
+
├── cli/reference.md # every CLI command, flag, env var
|
|
72
|
+
├── lxapp/
|
|
73
|
+
│ ├── guide.md # Page({}), useLxPage, events
|
|
74
|
+
│ ├── components.md # LxInput, LxVideo, LxPicker, LxMediaSwiper, LxNavigator
|
|
75
|
+
│ ├── lx-api.md # lx.* surface map + how to install @lingxia/types
|
|
76
|
+
│ └── bridge.md # setData, stream, channel mechanics
|
|
77
|
+
├── app/
|
|
78
|
+
│ ├── project.md # lingxia.yaml + macOS App UI
|
|
79
|
+
│ ├── apple-sdk.md # iOS/macOS SDK embedding
|
|
80
|
+
│ └── applinks.md # universal links / app links
|
|
81
|
+
├── native/development.md # Rust host: #[lingxia::native], HostAddon, facades
|
|
82
|
+
├── reference/
|
|
83
|
+
│ └── file-lifecycle.md # storage classes, downloadFile, FileManager
|
|
84
|
+
└── skill-manifest.json # package name + version + sync timestamp
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Where the content lives
|
|
88
|
+
|
|
89
|
+
The canonical source is in the LingXia monorepo at [`docs/skill/`](https://github.com/LingXia-Dev/LingXia/tree/main/docs/skill). At publish time, `scripts/sync.mjs` copies that tree into `skill/` inside this package. End users of `@lingxia/skill` consume the synced copy.
|
|
90
|
+
|
|
91
|
+
The skill itself doesn't depend on the LingXia repo at runtime — once installed, all cross-references are relative paths inside `skill/`.
|
|
92
|
+
|
|
93
|
+
## License
|
|
94
|
+
|
|
95
|
+
MIT
|
package/bin/install.mjs
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// `npx @lingxia/skill install [target]`
|
|
3
|
+
//
|
|
4
|
+
// Copies the LingXia agent skill into the chosen target. The skill is plain
|
|
5
|
+
// markdown — readable by Claude Code, Claude Agent SDK, OpenAI Codex, Cursor,
|
|
6
|
+
// or any tool that consumes project markdown — and ships in the layout that
|
|
7
|
+
// Anthropic Skills expect (`<target>/lingxia/SKILL.md` + relative sub-files).
|
|
8
|
+
//
|
|
9
|
+
// Default targets:
|
|
10
|
+
// - <cwd>/.claude/skills/lingxia/ (project-scoped, default)
|
|
11
|
+
// - ~/.claude/skills/lingxia/ (--user)
|
|
12
|
+
// - <path>/lingxia/ (--target <path>)
|
|
13
|
+
//
|
|
14
|
+
// For tools that look for a single AGENTS.md at the repo root (e.g. Codex CLI),
|
|
15
|
+
// pass --agents-md to also write a tiny pointer file at <cwd>/AGENTS.md that
|
|
16
|
+
// directs the agent at the installed SKILL.md.
|
|
17
|
+
//
|
|
18
|
+
// The skill source ships inside this package at `skill/`. It is synced from
|
|
19
|
+
// `docs/skill/` in the LingXia monorepo at publish time. Reinstalling is
|
|
20
|
+
// idempotent: existing target contents are removed first (unless --dry-run).
|
|
21
|
+
|
|
22
|
+
import { cp, mkdir, readFile, rm, stat, readdir, writeFile } from "node:fs/promises";
|
|
23
|
+
import { dirname, isAbsolute, join, resolve } from "node:path";
|
|
24
|
+
import { fileURLToPath } from "node:url";
|
|
25
|
+
import { homedir } from "node:os";
|
|
26
|
+
|
|
27
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
28
|
+
const pkgRoot = resolve(here, "..");
|
|
29
|
+
const source = join(pkgRoot, "skill");
|
|
30
|
+
|
|
31
|
+
function parseArgs(argv) {
|
|
32
|
+
const args = {
|
|
33
|
+
_: [],
|
|
34
|
+
user: false,
|
|
35
|
+
force: false,
|
|
36
|
+
dryRun: false,
|
|
37
|
+
target: null,
|
|
38
|
+
agentsMd: false,
|
|
39
|
+
};
|
|
40
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
41
|
+
const a = argv[i];
|
|
42
|
+
if (a === "--user") args.user = true;
|
|
43
|
+
else if (a === "--force") args.force = true;
|
|
44
|
+
else if (a === "--dry-run" || a === "-n") args.dryRun = true;
|
|
45
|
+
else if (a === "--target") args.target = argv[++i];
|
|
46
|
+
else if (a === "--agents-md") args.agentsMd = true;
|
|
47
|
+
else if (a === "--help" || a === "-h") args.help = true;
|
|
48
|
+
else if (a === "--version" || a === "-V") args.version = true;
|
|
49
|
+
else args._.push(a);
|
|
50
|
+
}
|
|
51
|
+
return args;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function printHelp() {
|
|
55
|
+
console.log(`Usage: npx @lingxia/skill <command> [options]
|
|
56
|
+
|
|
57
|
+
Install the LingXia agent skill into a project. The content is plain markdown
|
|
58
|
+
and works with any AI coding tool that reads project files (Claude Code,
|
|
59
|
+
Claude Agent SDK, OpenAI Codex, Cursor, ...).
|
|
60
|
+
|
|
61
|
+
Commands:
|
|
62
|
+
install Copy the skill into a target directory (default)
|
|
63
|
+
uninstall Remove an installed copy
|
|
64
|
+
where Print the path where install would write
|
|
65
|
+
version Print the skill version
|
|
66
|
+
|
|
67
|
+
Options:
|
|
68
|
+
--user Install into ~/.claude/skills/lingxia/ instead of <cwd>/.claude/skills/lingxia/
|
|
69
|
+
--target <path> Install into <path>/lingxia/ (overrides --user and cwd)
|
|
70
|
+
--agents-md Also write <cwd>/AGENTS.md pointing at the installed skill
|
|
71
|
+
(handy for Codex CLI and other tools that read AGENTS.md)
|
|
72
|
+
--force Overwrite an existing install without prompting (default in non-interactive shells)
|
|
73
|
+
--dry-run, -n Print actions without writing
|
|
74
|
+
-h, --help Show help
|
|
75
|
+
-V, --version Show version
|
|
76
|
+
|
|
77
|
+
Examples:
|
|
78
|
+
npx @lingxia/skill install # Anthropic-style: .claude/skills/lingxia/
|
|
79
|
+
npx @lingxia/skill install --user # ~/.claude/skills/lingxia/
|
|
80
|
+
npx @lingxia/skill install --agents-md # also write AGENTS.md (Codex-friendly)
|
|
81
|
+
npx @lingxia/skill install --target docs/agent --dry-run # custom target, preview only
|
|
82
|
+
npx @lingxia/skill uninstall
|
|
83
|
+
`);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async function readVersion() {
|
|
87
|
+
const pkg = JSON.parse(
|
|
88
|
+
await readFile(join(pkgRoot, "package.json"), "utf8")
|
|
89
|
+
);
|
|
90
|
+
return pkg.version;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async function exists(p) {
|
|
94
|
+
try {
|
|
95
|
+
await stat(p);
|
|
96
|
+
return true;
|
|
97
|
+
} catch {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function resolveTarget(args) {
|
|
103
|
+
if (args.target) {
|
|
104
|
+
const base = isAbsolute(args.target) ? args.target : resolve(process.cwd(), args.target);
|
|
105
|
+
return join(base, "lingxia");
|
|
106
|
+
}
|
|
107
|
+
if (args.user) {
|
|
108
|
+
return join(homedir(), ".claude", "skills", "lingxia");
|
|
109
|
+
}
|
|
110
|
+
return join(process.cwd(), ".claude", "skills", "lingxia");
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async function ensureSourceExists() {
|
|
114
|
+
if (!(await exists(source))) {
|
|
115
|
+
console.error(
|
|
116
|
+
`error: skill source missing at ${source}\n` +
|
|
117
|
+
" this package may not have been built/synced before publish."
|
|
118
|
+
);
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async function install(args) {
|
|
124
|
+
await ensureSourceExists();
|
|
125
|
+
const target = resolveTarget(args);
|
|
126
|
+
const targetExists = await exists(target);
|
|
127
|
+
const agentsMdPath = args.agentsMd ? join(process.cwd(), "AGENTS.md") : null;
|
|
128
|
+
|
|
129
|
+
console.log(`source: ${source}`);
|
|
130
|
+
console.log(`target: ${target}`);
|
|
131
|
+
if (agentsMdPath) console.log(`agents-md: ${agentsMdPath}`);
|
|
132
|
+
|
|
133
|
+
if (args.dryRun) {
|
|
134
|
+
console.log("[dry-run] would " + (targetExists ? "replace" : "create") + " target");
|
|
135
|
+
if (agentsMdPath) {
|
|
136
|
+
console.log(
|
|
137
|
+
"[dry-run] would " +
|
|
138
|
+
((await exists(agentsMdPath)) ? "append to" : "write") +
|
|
139
|
+
" AGENTS.md"
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (targetExists) {
|
|
146
|
+
await rm(target, { recursive: true, force: true });
|
|
147
|
+
}
|
|
148
|
+
await mkdir(target, { recursive: true });
|
|
149
|
+
await cp(source, target, { recursive: true });
|
|
150
|
+
|
|
151
|
+
if (agentsMdPath) {
|
|
152
|
+
await writeAgentsMd(agentsMdPath, target);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
console.log(`installed @lingxia/skill v${await readVersion()} → ${target}`);
|
|
156
|
+
console.log(
|
|
157
|
+
"The skill is plain markdown; open SKILL.md or point your AI coding tool at the install directory."
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
async function writeAgentsMd(agentsMdPath, skillTarget) {
|
|
162
|
+
const skillRelative = skillTarget.startsWith(process.cwd() + "/")
|
|
163
|
+
? skillTarget.slice(process.cwd().length + 1)
|
|
164
|
+
: skillTarget;
|
|
165
|
+
const marker = "<!-- @lingxia/skill: AGENTS.md pointer -->";
|
|
166
|
+
const block = `${marker}
|
|
167
|
+
## LingXia
|
|
168
|
+
|
|
169
|
+
This project uses the LingXia cross-platform app framework. The development
|
|
170
|
+
skill — decision tree, recipes, CLI / component / native API references —
|
|
171
|
+
lives at:
|
|
172
|
+
|
|
173
|
+
${skillRelative}/SKILL.md
|
|
174
|
+
|
|
175
|
+
Start there. Sub-references are linked from that file using relative paths.
|
|
176
|
+
${marker}
|
|
177
|
+
`;
|
|
178
|
+
|
|
179
|
+
if (await exists(agentsMdPath)) {
|
|
180
|
+
const existing = await readFile(agentsMdPath, "utf8");
|
|
181
|
+
if (existing.includes(marker)) {
|
|
182
|
+
// Replace the previous block in-place.
|
|
183
|
+
const re = new RegExp(
|
|
184
|
+
`${marker}[\\s\\S]*?${marker}\\n?`,
|
|
185
|
+
"g"
|
|
186
|
+
);
|
|
187
|
+
await writeFile(agentsMdPath, existing.replace(re, block));
|
|
188
|
+
} else {
|
|
189
|
+
const sep = existing.endsWith("\n") ? "\n" : "\n\n";
|
|
190
|
+
await writeFile(agentsMdPath, existing + sep + block);
|
|
191
|
+
}
|
|
192
|
+
} else {
|
|
193
|
+
await writeFile(agentsMdPath, `# AGENTS\n\n${block}`);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
async function uninstall(args) {
|
|
198
|
+
const target = resolveTarget(args);
|
|
199
|
+
if (!(await exists(target))) {
|
|
200
|
+
console.log(`nothing to uninstall at ${target}`);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
if (args.dryRun) {
|
|
204
|
+
console.log(`[dry-run] would remove ${target}`);
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
await rm(target, { recursive: true, force: true });
|
|
208
|
+
console.log(`removed ${target}`);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
async function where(args) {
|
|
212
|
+
console.log(resolveTarget(args));
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
async function main() {
|
|
216
|
+
const argv = process.argv.slice(2);
|
|
217
|
+
const args = parseArgs(argv);
|
|
218
|
+
|
|
219
|
+
if (args.help) return printHelp();
|
|
220
|
+
if (args.version) {
|
|
221
|
+
console.log(await readVersion());
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const cmd = args._[0] ?? "install";
|
|
226
|
+
|
|
227
|
+
switch (cmd) {
|
|
228
|
+
case "install":
|
|
229
|
+
return install(args);
|
|
230
|
+
case "uninstall":
|
|
231
|
+
return uninstall(args);
|
|
232
|
+
case "where":
|
|
233
|
+
return where(args);
|
|
234
|
+
case "version":
|
|
235
|
+
console.log(await readVersion());
|
|
236
|
+
return;
|
|
237
|
+
default:
|
|
238
|
+
console.error(`unknown command: ${cmd}\n`);
|
|
239
|
+
printHelp();
|
|
240
|
+
process.exit(2);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
main().catch((err) => {
|
|
245
|
+
console.error(err);
|
|
246
|
+
process.exit(1);
|
|
247
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lingxia/skill",
|
|
3
|
+
"version": "0.8.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"description": "Agent skill for the LingXia cross-platform app framework. Installs a markdown skill bundle into your project so AI coding tools — Claude Code, Claude Agent SDK, OpenAI Codex, Cursor, or any agent that reads project markdown — can build lxapps, host apps, and native Rust extensions on LingXia.",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"lingxia-skill": "bin/install.mjs"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"bin",
|
|
12
|
+
"skill",
|
|
13
|
+
"scripts/sync.mjs",
|
|
14
|
+
"README.md"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"sync": "node scripts/sync.mjs",
|
|
18
|
+
"prepack": "node scripts/sync.mjs"
|
|
19
|
+
},
|
|
20
|
+
"keywords": [
|
|
21
|
+
"lingxia",
|
|
22
|
+
"lxapp",
|
|
23
|
+
"cross-platform",
|
|
24
|
+
"ai-tooling",
|
|
25
|
+
"agent-skill",
|
|
26
|
+
"skill",
|
|
27
|
+
"claude-code",
|
|
28
|
+
"claude-agent-sdk",
|
|
29
|
+
"codex",
|
|
30
|
+
"cursor"
|
|
31
|
+
],
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "https://github.com/LingXia-Dev/LingXia.git",
|
|
35
|
+
"directory": "packages/lingxia-skill"
|
|
36
|
+
},
|
|
37
|
+
"homepage": "https://github.com/LingXia-Dev/LingXia/tree/main/packages/lingxia-skill",
|
|
38
|
+
"bugs": {
|
|
39
|
+
"url": "https://github.com/LingXia-Dev/LingXia/issues"
|
|
40
|
+
},
|
|
41
|
+
"publishConfig": {
|
|
42
|
+
"registry": "https://registry.npmjs.org",
|
|
43
|
+
"access": "public"
|
|
44
|
+
},
|
|
45
|
+
"license": "MIT",
|
|
46
|
+
"engines": {
|
|
47
|
+
"node": ">=18"
|
|
48
|
+
}
|
|
49
|
+
}
|
package/scripts/sync.mjs
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Sync the canonical skill source from docs/skill/ into packages/lingxia-skill/skill/.
|
|
3
|
+
//
|
|
4
|
+
// Run during `npm run sync` (manual) and `prepack` (automatic before publish).
|
|
5
|
+
// The published tarball contains only the synced `skill/` copy — the canonical
|
|
6
|
+
// `docs/skill/` source is not part of the published package.
|
|
7
|
+
//
|
|
8
|
+
// If the canonical source is not reachable (e.g. consumers running scripts
|
|
9
|
+
// from the installed package), the sync is skipped silently — the `skill/`
|
|
10
|
+
// directory already shipped in the tarball is authoritative.
|
|
11
|
+
|
|
12
|
+
import { cp, mkdir, rm, stat, readFile, writeFile } from "node:fs/promises";
|
|
13
|
+
import { dirname, join, resolve } from "node:path";
|
|
14
|
+
import { fileURLToPath } from "node:url";
|
|
15
|
+
|
|
16
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
17
|
+
const pkgRoot = resolve(here, "..");
|
|
18
|
+
const repoRoot = resolve(pkgRoot, "..", "..");
|
|
19
|
+
const source = join(repoRoot, "docs", "skill");
|
|
20
|
+
const target = join(pkgRoot, "skill");
|
|
21
|
+
|
|
22
|
+
async function exists(p) {
|
|
23
|
+
try {
|
|
24
|
+
await stat(p);
|
|
25
|
+
return true;
|
|
26
|
+
} catch {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async function main() {
|
|
32
|
+
if (!(await exists(source))) {
|
|
33
|
+
// Running outside the monorepo (e.g. inside an installed @lingxia/skill
|
|
34
|
+
// tarball). Nothing to sync; ship as-is.
|
|
35
|
+
console.log(
|
|
36
|
+
`[sync] skipped — source not found at ${source} (expected when running from an installed package)`
|
|
37
|
+
);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (await exists(target)) {
|
|
42
|
+
await rm(target, { recursive: true, force: true });
|
|
43
|
+
}
|
|
44
|
+
await mkdir(target, { recursive: true });
|
|
45
|
+
await cp(source, target, { recursive: true });
|
|
46
|
+
|
|
47
|
+
// Write a small manifest so consumers can introspect the skill version
|
|
48
|
+
// without reading SKILL.md.
|
|
49
|
+
const pkgJson = JSON.parse(
|
|
50
|
+
await readFile(join(pkgRoot, "package.json"), "utf8")
|
|
51
|
+
);
|
|
52
|
+
const manifest = {
|
|
53
|
+
name: pkgJson.name,
|
|
54
|
+
version: pkgJson.version,
|
|
55
|
+
entry: "SKILL.md",
|
|
56
|
+
syncedAt: new Date().toISOString(),
|
|
57
|
+
};
|
|
58
|
+
await writeFile(
|
|
59
|
+
join(target, "skill-manifest.json"),
|
|
60
|
+
JSON.stringify(manifest, null, 2) + "\n"
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
console.log(`[sync] copied ${source} → ${target}`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
main().catch((err) => {
|
|
67
|
+
console.error(err);
|
|
68
|
+
process.exit(1);
|
|
69
|
+
});
|