@anvia/cli 1.2.0 → 1.3.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.
Files changed (53) hide show
  1. package/README.md +32 -0
  2. package/dist/chunk-ERCP4EIZ.js +607 -0
  3. package/dist/chunk-ERCP4EIZ.js.map +1 -0
  4. package/dist/cli.js +278 -74
  5. package/dist/cli.js.map +1 -1
  6. package/dist/index.d.ts +54 -1
  7. package/dist/index.js +21 -3
  8. package/dist/skills/anvia-agent/SKILL.md +59 -0
  9. package/dist/skills/anvia-agent/references/agent-options.md +83 -0
  10. package/dist/skills/anvia-agent/references/providers.md +22 -0
  11. package/dist/skills/anvia-agent/references/teams.md +84 -0
  12. package/dist/skills/anvia-agent/references/tools.md +71 -0
  13. package/dist/skills/anvia-agent/scripts/check-agent.sh +99 -0
  14. package/dist/skills/anvia-channels/SKILL.md +59 -0
  15. package/dist/skills/anvia-channels/references/adapters.md +53 -0
  16. package/dist/skills/anvia-channels/references/channel-agent.md +52 -0
  17. package/dist/skills/anvia-channels/references/delivery.md +53 -0
  18. package/dist/skills/anvia-channels/scripts/check-channels.sh +79 -0
  19. package/dist/skills/anvia-chat/SKILL.md +61 -0
  20. package/dist/skills/anvia-chat/references/react-ui.md +104 -0
  21. package/dist/skills/anvia-chat/references/server-protocol.md +53 -0
  22. package/dist/skills/anvia-chat/references/transports-state.md +71 -0
  23. package/dist/skills/anvia-chat/scripts/check-chat-boundary.sh +70 -0
  24. package/dist/skills/anvia-evals/SKILL.md +51 -0
  25. package/dist/skills/anvia-evals/references/judges.md +61 -0
  26. package/dist/skills/anvia-evals/references/metrics.md +45 -0
  27. package/dist/skills/anvia-evals/references/running.md +62 -0
  28. package/dist/skills/anvia-evals/scripts/check-evals.sh +73 -0
  29. package/dist/skills/anvia-mcp/SKILL.md +49 -0
  30. package/dist/skills/anvia-mcp/references/clients.md +73 -0
  31. package/dist/skills/anvia-mcp/references/safety.md +25 -0
  32. package/dist/skills/anvia-mcp/scripts/check-mcp.sh +73 -0
  33. package/dist/skills/anvia-pipeline/SKILL.md +52 -0
  34. package/dist/skills/anvia-pipeline/references/agents-extract.md +62 -0
  35. package/dist/skills/anvia-pipeline/references/steps-compose.md +63 -0
  36. package/dist/skills/anvia-pipeline/scripts/check-pipeline.sh +69 -0
  37. package/dist/skills/anvia-rag/SKILL.md +50 -0
  38. package/dist/skills/anvia-rag/references/graph-rag.md +115 -0
  39. package/dist/skills/anvia-rag/references/pipeline.md +81 -0
  40. package/dist/skills/anvia-rag/references/rag-tool.md +43 -0
  41. package/dist/skills/anvia-rag/references/stores.md +68 -0
  42. package/dist/skills/anvia-rag/scripts/check-rag.sh +75 -0
  43. package/dist/skills/anvia-studio/SKILL.md +45 -0
  44. package/dist/skills/anvia-studio/references/inspect.md +33 -0
  45. package/dist/skills/anvia-studio/references/observe.md +34 -0
  46. package/dist/skills/anvia-studio/references/serve.md +65 -0
  47. package/dist/skills/anvia-studio/scripts/check-studio.sh +59 -0
  48. package/dist/skills/release-notes/SKILL.md +18 -0
  49. package/dist/skills/release-notes/references/style.md +6 -0
  50. package/dist/skills/release-notes/scripts/draft.sh +22 -0
  51. package/package.json +2 -2
  52. package/dist/chunk-IH4ZNWHB.js +0 -285
  53. package/dist/chunk-IH4ZNWHB.js.map +0 -1
package/README.md CHANGED
@@ -29,3 +29,35 @@ Without `--overwrite`, `update` is a preview: it reports each file as `up-to-dat
29
29
  to write the registry content over out-of-date and missing files of installed components.
30
30
  `update` never installs new items — use `add` for that. Locally edited copies are
31
31
  overwritten, so commit or stash your changes first.
32
+
33
+ ## Agent Skills
34
+
35
+ Scaffold the Anvia Agent Skills — curated, API-verified knowledge for building with
36
+ Anvia (agents, chat, RAG, MCP, pipelines, Studio, evals, channels) — so your coding
37
+ agent writes correct Anvia code:
38
+
39
+ ```sh
40
+ pnpm dlx @anvia/cli skills list # show the available skills
41
+ pnpm dlx @anvia/cli skills init # copy every skill into ./skills
42
+ pnpm dlx @anvia/cli skills init --claude --agents # also wire Claude Code and AGENTS.md
43
+ pnpm dlx @anvia/cli skills init --codex --cursor # --codex is an AGENTS.md alias
44
+ pnpm dlx @anvia/cli skills update # compare installed skills (preview only)
45
+ pnpm dlx @anvia/cli skills update --force
46
+ ```
47
+
48
+ `skills init` always writes the canonical skills into `./skills` (override with
49
+ `--dir <path>`) — one folder per skill (`SKILL.md` + `references/` + `scripts/`), with
50
+ the executable bit preserved on skill scripts — and prints the wiring snippet. Generated
51
+ adapters and the snippet always reference the effective directory. The
52
+ target flags add adapters on top:
53
+
54
+ - `--claude` copies the skills into `.claude/skills/`, where Claude Code loads them natively.
55
+ - `--cursor` writes one on-demand Cursor rule per skill into `.cursor/rules/` that points
56
+ at the matching `skills/<name>/SKILL.md`.
57
+ - `--agents` (and its alias `--codex`) maintains an `AGENTS.md` section, between
58
+ `anvia-skills` markers, listing every skill with its description. Existing
59
+ `AGENTS.md` content outside the markers is never touched.
60
+
61
+ Skills you have locally edited are left alone unless you pass `--force`. `skills update`
62
+ refreshes installed skills and adapters in place and never reinstalls skills that were
63
+ removed from the project. Both commands accept `--cwd <path>`.
@@ -0,0 +1,607 @@
1
+ // src/registry.ts
2
+ import { spawnSync } from "child_process";
3
+ import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "fs";
4
+ import { createRequire } from "module";
5
+ import { tmpdir } from "os";
6
+ import { basename, dirname, join } from "path";
7
+ import { fileURLToPath } from "url";
8
+ var registryItemNames = [
9
+ "chat",
10
+ "thread",
11
+ "message",
12
+ "composer",
13
+ "attachment",
14
+ "markdown",
15
+ "tool-fallback"
16
+ ];
17
+ var itemFiles = {
18
+ attachment: ["attachment.tsx"],
19
+ chat: [
20
+ "attachment.tsx",
21
+ "markdown.tsx",
22
+ "tool-fallback.tsx",
23
+ "message.tsx",
24
+ "composer.tsx",
25
+ "thread.tsx",
26
+ "chat.tsx"
27
+ ],
28
+ composer: ["attachment.tsx", "composer.tsx"],
29
+ markdown: ["markdown.tsx"],
30
+ message: ["attachment.tsx", "markdown.tsx", "tool-fallback.tsx", "message.tsx"],
31
+ thread: ["attachment.tsx", "markdown.tsx", "tool-fallback.tsx", "message.tsx", "thread.tsx"],
32
+ "tool-fallback": ["tool-fallback.tsx"]
33
+ };
34
+ var revealCss = {
35
+ "@keyframes anvia-stream-gradient-settle": {
36
+ to: {
37
+ opacity: "1"
38
+ }
39
+ },
40
+ "@layer components": {
41
+ '.anvia-markdown [data-state="revealing"]': {
42
+ animation: "anvia-stream-gradient-settle var(--anvia-stream-reveal-duration, 180ms) linear both",
43
+ opacity: "var(--anvia-stream-reveal-opacity, 1)"
44
+ },
45
+ "@media (prefers-reduced-motion: reduce)": {
46
+ '.anvia-markdown [data-state="revealing"]': {
47
+ animation: "none",
48
+ opacity: "1"
49
+ }
50
+ }
51
+ }
52
+ };
53
+ function createRegistryItem(name, options = {}) {
54
+ const packageVersion = options.packageVersion ?? currentPackageVersion();
55
+ const registryDirectory = options.registryDirectory ?? bundledRegistryDirectory();
56
+ const files = itemFiles[name].map((filename) => ({
57
+ content: readFileSync(join(registryDirectory, filename), "utf8"),
58
+ path: `registry/anvia/${filename}`,
59
+ target: `@components/anvia/${filename}`,
60
+ type: "registry:component"
61
+ }));
62
+ const item = {
63
+ $schema: "https://ui.shadcn.com/schema/registry-item.json",
64
+ dependencies: [`@anvia/react-ui@${packageVersion}`],
65
+ description: registryItemDescription(name),
66
+ files,
67
+ name,
68
+ title: `Anvia ${name}`,
69
+ type: files.length === 1 ? "registry:component" : "registry:block"
70
+ };
71
+ if (name === "chat" || name === "markdown" || name === "message" || name === "thread") {
72
+ item.css = revealCss;
73
+ }
74
+ return item;
75
+ }
76
+ function initializeProject(options = {}) {
77
+ const cwd = options.cwd ?? process.cwd();
78
+ const args = ["init", "--cwd", cwd, "--yes", "--no-monorepo", "--base", "radix"];
79
+ if (options.template !== void 0) {
80
+ args.push("--template", options.template);
81
+ }
82
+ if (options.force === true) {
83
+ args.push("--force");
84
+ }
85
+ runShadcn(args);
86
+ }
87
+ function addRegistryItem(name, options = {}) {
88
+ const cwd = options.cwd ?? process.cwd();
89
+ const temporaryDirectory = mkdtempSync(join(tmpdir(), "anvia-registry-"));
90
+ const itemPath = join(temporaryDirectory, `${name}.json`);
91
+ try {
92
+ writeFileSync(itemPath, `${JSON.stringify(createRegistryItem(name), null, 2)}
93
+ `);
94
+ const args = ["add", itemPath, "--cwd", cwd, "--yes"];
95
+ if (options.overwrite === true) {
96
+ args.push("--overwrite");
97
+ }
98
+ runShadcn(args);
99
+ } finally {
100
+ rmSync(temporaryDirectory, { force: true, recursive: true });
101
+ }
102
+ }
103
+ function isRegistryItemName(value) {
104
+ return registryItemNames.includes(value);
105
+ }
106
+ function inspectInstalledItems(options = {}) {
107
+ const names = options.items ?? registryItemNames;
108
+ const directory = componentsDirectory(options);
109
+ const registry = options.registryDirectory ?? bundledRegistryDirectory();
110
+ return names.map((name) => {
111
+ const files = itemFiles[name].map((filename) => {
112
+ const path = join(directory, "anvia", filename);
113
+ let status = "missing";
114
+ if (existsSync(path)) {
115
+ const installed = readFileSync(path, "utf8");
116
+ status = installed === registryFileContent(registry, filename) ? "up-to-date" : "modified";
117
+ }
118
+ return { filename, path, status };
119
+ });
120
+ return {
121
+ name,
122
+ installed: files.some((file) => file.status !== "missing"),
123
+ complete: files.every((file) => file.status !== "missing"),
124
+ files
125
+ };
126
+ });
127
+ }
128
+ function updateInstalledItems(options = {}) {
129
+ const registry = options.registryDirectory ?? bundledRegistryDirectory();
130
+ const report = inspectInstalledItems(options);
131
+ const updated = [];
132
+ if (options.overwrite === true) {
133
+ const actionable = /* @__PURE__ */ new Map();
134
+ for (const item of report) {
135
+ if (!item.installed) {
136
+ continue;
137
+ }
138
+ for (const file of item.files) {
139
+ if (file.status === "up-to-date" || actionable.has(file.path)) {
140
+ continue;
141
+ }
142
+ actionable.set(file.path, registryFileContent(registry, file.filename));
143
+ }
144
+ }
145
+ for (const [path, content] of actionable) {
146
+ mkdirSync(dirname(path), { recursive: true });
147
+ writeFileSync(path, content);
148
+ updated.push(path);
149
+ }
150
+ }
151
+ return { report, updated };
152
+ }
153
+ function closestRegistryItemName(value) {
154
+ let best;
155
+ let bestDistance = Number.POSITIVE_INFINITY;
156
+ for (const name of registryItemNames) {
157
+ const distance = levenshteinDistance(value, name);
158
+ if (distance < bestDistance) {
159
+ bestDistance = distance;
160
+ best = name;
161
+ }
162
+ }
163
+ return bestDistance <= 2 ? best : void 0;
164
+ }
165
+ function componentsDirectory(options = {}) {
166
+ const cwd = options.cwd ?? process.cwd();
167
+ const manifestPath = join(cwd, "components.json");
168
+ let manifest;
169
+ try {
170
+ manifest = JSON.parse(stripJsonComments(readFileSync(manifestPath, "utf8")));
171
+ } catch {
172
+ throw new Error(`Missing or unreadable components.json in ${cwd}. Run \`anvia init\` first.`);
173
+ }
174
+ const alias = manifest.aliases?.components ?? "@/components";
175
+ return resolveAliasDirectory(cwd, alias);
176
+ }
177
+ function resolveAliasDirectory(cwd, alias) {
178
+ for (const configName of ["tsconfig.json", "jsconfig.json"]) {
179
+ let config;
180
+ try {
181
+ config = JSON.parse(stripJsonComments(readFileSync(join(cwd, configName), "utf8")));
182
+ } catch {
183
+ continue;
184
+ }
185
+ const paths = config.compilerOptions?.paths;
186
+ if (paths === void 0) {
187
+ continue;
188
+ }
189
+ const target = resolveAliasTarget(alias, paths);
190
+ if (target === void 0) {
191
+ continue;
192
+ }
193
+ return join(cwd, config.compilerOptions?.baseUrl ?? ".", target);
194
+ }
195
+ throw new Error(
196
+ `Cannot resolve the components alias "${alias}" from tsconfig.json or jsconfig.json in ${cwd}.`
197
+ );
198
+ }
199
+ function resolveAliasTarget(alias, paths) {
200
+ for (const [key, targets] of Object.entries(paths)) {
201
+ const target = targets[0];
202
+ if (target === void 0) {
203
+ continue;
204
+ }
205
+ if (key.endsWith("/*")) {
206
+ const prefix = key.slice(0, -2);
207
+ if (alias === prefix) {
208
+ return target.replace(/\/\*$/, "");
209
+ }
210
+ if (alias.startsWith(`${prefix}/`)) {
211
+ return join(target.replace(/\/\*$/, ""), alias.slice(prefix.length + 1));
212
+ }
213
+ } else if (key === alias) {
214
+ return target;
215
+ }
216
+ }
217
+ return void 0;
218
+ }
219
+ function stripJsonComments(source) {
220
+ let result = "";
221
+ let inString = false;
222
+ let escaped = false;
223
+ for (let index = 0; index < source.length; index += 1) {
224
+ const character = source[index];
225
+ if (inString) {
226
+ result += character;
227
+ if (escaped) {
228
+ escaped = false;
229
+ } else if (character === "\\") {
230
+ escaped = true;
231
+ } else if (character === '"') {
232
+ inString = false;
233
+ }
234
+ continue;
235
+ }
236
+ if (character === '"') {
237
+ inString = true;
238
+ result += character;
239
+ continue;
240
+ }
241
+ if (character === "/" && source[index + 1] === "/") {
242
+ while (index < source.length && source[index] !== "\n") {
243
+ index += 1;
244
+ }
245
+ continue;
246
+ }
247
+ result += character;
248
+ }
249
+ return result;
250
+ }
251
+ function registryFileContent(registryDirectory, filename) {
252
+ return readFileSync(join(registryDirectory, filename), "utf8");
253
+ }
254
+ function levenshteinDistance(left, right) {
255
+ const previous = Array.from({ length: right.length + 1 }, (_, index) => index);
256
+ for (let i = 1; i <= left.length; i += 1) {
257
+ let diagonal = previous[0];
258
+ previous[0] = i;
259
+ for (let j = 1; j <= right.length; j += 1) {
260
+ const above = previous[j];
261
+ previous[j] = Math.min(
262
+ above + 1,
263
+ previous[j - 1] + 1,
264
+ diagonal + (left[i - 1] === right[j - 1] ? 0 : 1)
265
+ );
266
+ diagonal = above;
267
+ }
268
+ }
269
+ return previous[right.length];
270
+ }
271
+ function runShadcn(args) {
272
+ const require2 = createRequire(import.meta.url);
273
+ const shadcnEntry = require2.resolve("shadcn");
274
+ const result = spawnSync(process.execPath, [shadcnEntry, ...args], {
275
+ encoding: "utf8",
276
+ stdio: "inherit"
277
+ });
278
+ if (result.error !== void 0) {
279
+ throw result.error;
280
+ }
281
+ if (result.status !== 0) {
282
+ throw new Error(`shadcn ${args[0] ?? "command"} failed with exit code ${result.status}.`);
283
+ }
284
+ }
285
+ function bundledRegistryDirectory() {
286
+ return fileURLToPath(new URL("./registry/", import.meta.url));
287
+ }
288
+ function currentPackageVersion() {
289
+ const manifestPath = fileURLToPath(new URL("../package.json", import.meta.url));
290
+ const manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
291
+ if (typeof manifest.version !== "string") {
292
+ throw new Error(`Missing package version in ${basename(manifestPath)}.`);
293
+ }
294
+ return manifest.version;
295
+ }
296
+ function registryItemDescription(name) {
297
+ if (name === "chat") {
298
+ return "A complete editable Anvia chat interface.";
299
+ }
300
+ return `Editable Anvia ${name} UI.`;
301
+ }
302
+
303
+ // src/skills/discovery.ts
304
+ import { readdirSync, readFileSync as readFileSync2 } from "fs";
305
+ import { join as join2 } from "path";
306
+ import { fileURLToPath as fileURLToPath2 } from "url";
307
+ function bundledSkillsDirectory() {
308
+ return fileURLToPath2(new URL("./skills/", import.meta.url));
309
+ }
310
+ function skillNames(options = {}) {
311
+ const directory = options.skillsDirectory ?? bundledSkillsDirectory();
312
+ return readdirSync(directory, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort();
313
+ }
314
+ function skillsTargetDirectory(options = {}) {
315
+ const cwd = options.cwd ?? process.cwd();
316
+ return join2(cwd, options.dir ?? "skills");
317
+ }
318
+ function collectSkillFiles(root) {
319
+ const files = [];
320
+ const visit = (directory, prefix) => {
321
+ const entries = readdirSync(directory, { withFileTypes: true }).sort(
322
+ (left, right) => left.name.localeCompare(right.name)
323
+ );
324
+ for (const entry of entries) {
325
+ if (entry.isDirectory()) {
326
+ visit(join2(directory, entry.name), `${prefix}${entry.name}/`);
327
+ } else if (entry.isFile()) {
328
+ files.push(`${prefix}${entry.name}`);
329
+ }
330
+ }
331
+ };
332
+ visit(root, "");
333
+ return files;
334
+ }
335
+ function skillDescription(skillsDirectory, name) {
336
+ const source = readFileSync2(join2(skillsDirectory, name, "SKILL.md"), "utf8");
337
+ return (/^description:\s*(.+)$/m.exec(source)?.[1] ?? "").trim();
338
+ }
339
+
340
+ // src/skills/write.ts
341
+ import { existsSync as existsSync5, readFileSync as readFileSync6 } from "fs";
342
+ import { join as join6 } from "path";
343
+
344
+ // src/skills/adapters/agents-doc.ts
345
+ import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
346
+ import { dirname as dirname2 } from "path";
347
+ var agentsSectionStart = "<!-- anvia-skills:start -->";
348
+ var agentsSectionEnd = "<!-- anvia-skills:end -->";
349
+ function syncAgentsDoc(options) {
350
+ const section = agentsSkillsSection(options.skillsDirectory, options.canonicalDir);
351
+ if (!existsSync2(options.agentsPath)) {
352
+ mkdirSync2(dirname2(options.agentsPath), { recursive: true });
353
+ writeFileSync2(options.agentsPath, `# AGENTS.md
354
+
355
+ ${section}
356
+ `);
357
+ return { created: [options.agentsPath], updated: [], skipped: 0 };
358
+ }
359
+ const existing = readFileSync3(options.agentsPath, "utf8");
360
+ const startIndex = existing.indexOf(agentsSectionStart);
361
+ const endIndex = existing.indexOf(agentsSectionEnd);
362
+ let next;
363
+ if (startIndex === -1 || endIndex === -1) {
364
+ next = `${existing.replace(/\n+$/, "")}
365
+
366
+ ${section}
367
+ `;
368
+ } else {
369
+ next = existing.slice(0, startIndex) + section + existing.slice(endIndex + agentsSectionEnd.length);
370
+ }
371
+ if (next === existing) {
372
+ return { created: [], updated: [], skipped: 0 };
373
+ }
374
+ writeFileSync2(options.agentsPath, next);
375
+ return { created: [], updated: [options.agentsPath], skipped: 0 };
376
+ }
377
+ function agentsSkillsSection(skillsDirectory, canonicalDir) {
378
+ const lines = skillNames({ skillsDirectory }).map(
379
+ (name) => `- \`${canonicalDir}/${name}/SKILL.md\` \u2014 ${skillDescription(skillsDirectory, name)}`
380
+ );
381
+ return [
382
+ agentsSectionStart,
383
+ "## Anvia Agent Skills",
384
+ "",
385
+ `This project keeps Anvia Agent Skills in \`${canonicalDir}/\`. When a task matches a skill, read`,
386
+ "its `SKILL.md` first and follow it, including the `references/` files and `scripts/`",
387
+ "it points to. Run skill scripts with `sh` when they help verify the work.",
388
+ "",
389
+ ...lines,
390
+ agentsSectionEnd
391
+ ].join("\n");
392
+ }
393
+
394
+ // src/skills/adapters/claude.ts
395
+ import { join as join4 } from "path";
396
+
397
+ // src/skills/adapters/skill-tree.ts
398
+ import { chmodSync, existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync4, statSync, writeFileSync as writeFileSync3 } from "fs";
399
+ import { dirname as dirname3, join as join3 } from "path";
400
+ function syncSkillTree(options) {
401
+ const created = [];
402
+ const updated = [];
403
+ let skipped = 0;
404
+ for (const name of skillNames({ skillsDirectory: options.skillsDirectory })) {
405
+ const sourceRoot = join3(options.skillsDirectory, name);
406
+ const targetRoot = join3(options.targetRoot, name);
407
+ const sourceFiles = collectSkillFiles(sourceRoot);
408
+ if (options.mode === "update" && !isSkillInstalled(targetRoot, sourceFiles)) {
409
+ continue;
410
+ }
411
+ for (const relativePath of sourceFiles) {
412
+ const outcome = syncOneFile(sourceRoot, targetRoot, relativePath, options);
413
+ if (outcome === "created") {
414
+ created.push(join3(targetRoot, relativePath));
415
+ } else if (outcome === "updated") {
416
+ updated.push(join3(targetRoot, relativePath));
417
+ } else if (outcome === "skipped") {
418
+ skipped += 1;
419
+ }
420
+ }
421
+ }
422
+ return { created, updated, skipped };
423
+ }
424
+ function isSkillInstalled(targetRoot, sourceFiles) {
425
+ return sourceFiles.some((file) => existsSync3(join3(targetRoot, file)));
426
+ }
427
+ function syncOneFile(sourceRoot, targetRoot, relativePath, options) {
428
+ const sourcePath = join3(sourceRoot, relativePath);
429
+ const targetPath = join3(targetRoot, relativePath);
430
+ const content = readFileSync4(sourcePath, "utf8");
431
+ if (!existsSync3(targetPath)) {
432
+ if (options.mode === "update" && !options.force) {
433
+ return "skipped";
434
+ }
435
+ mkdirSync3(dirname3(targetPath), { recursive: true });
436
+ writeFileSync3(targetPath, content);
437
+ chmodSync(targetPath, statSync(sourcePath).mode & 511);
438
+ return "created";
439
+ }
440
+ if (readFileSync4(targetPath, "utf8") === content) {
441
+ return "unchanged";
442
+ }
443
+ if (!options.force) {
444
+ return "skipped";
445
+ }
446
+ writeFileSync3(targetPath, content);
447
+ chmodSync(targetPath, statSync(sourcePath).mode & 511);
448
+ return "updated";
449
+ }
450
+
451
+ // src/skills/adapters/claude.ts
452
+ function syncClaudeSkills(options) {
453
+ return syncSkillTree({
454
+ skillsDirectory: options.skillsDirectory,
455
+ targetRoot: join4(options.cwd, ".claude", "skills"),
456
+ force: options.force,
457
+ mode: options.mode
458
+ });
459
+ }
460
+
461
+ // src/skills/adapters/cursor-rules.ts
462
+ import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
463
+ import { dirname as dirname4, join as join5 } from "path";
464
+ function syncCursorRules(options) {
465
+ const created = [];
466
+ const updated = [];
467
+ let skipped = 0;
468
+ for (const name of skillNames({ skillsDirectory: options.skillsDirectory })) {
469
+ const targetPath = join5(options.rulesDirectory, `${name}.mdc`);
470
+ const content = cursorRuleContent(options.skillsDirectory, name, options.canonicalDir);
471
+ if (!existsSync4(targetPath)) {
472
+ if (options.mode === "update" && !options.force) {
473
+ continue;
474
+ }
475
+ mkdirSync4(dirname4(targetPath), { recursive: true });
476
+ writeFileSync4(targetPath, content);
477
+ created.push(targetPath);
478
+ continue;
479
+ }
480
+ if (readFileSync5(targetPath, "utf8") === content) {
481
+ continue;
482
+ }
483
+ if (!options.force) {
484
+ skipped += 1;
485
+ continue;
486
+ }
487
+ writeFileSync4(targetPath, content);
488
+ updated.push(targetPath);
489
+ }
490
+ return { created, updated, skipped };
491
+ }
492
+ function cursorRuleContent(skillsDirectory, name, canonicalDir) {
493
+ const description = skillDescription(skillsDirectory, name).replace(/"/g, '\\"');
494
+ return [
495
+ "---",
496
+ `description: "${description}"`,
497
+ "alwaysApply: false",
498
+ "---",
499
+ "",
500
+ `When a task matches this skill, read \`${canonicalDir}/${name}/SKILL.md\` at the project root and`,
501
+ "follow it, including the `references/` files and `scripts/` it points to. Run skill",
502
+ "scripts with `sh` when they help verify the work.",
503
+ ""
504
+ ].join("\n");
505
+ }
506
+
507
+ // src/skills/write.ts
508
+ function inspectInstalledSkills(options = {}) {
509
+ const skillsDirectory = options.skillsDirectory ?? bundledSkillsDirectory();
510
+ const target = skillsTargetDirectory(options);
511
+ return skillNames({ skillsDirectory }).map((name) => {
512
+ const files = collectSkillFiles(join6(skillsDirectory, name)).map((relativePath) => {
513
+ const path = join6(target, name, relativePath);
514
+ let status = "missing";
515
+ if (existsSync5(path)) {
516
+ const installed = readFileSync6(path, "utf8");
517
+ const source = readFileSync6(join6(skillsDirectory, name, relativePath), "utf8");
518
+ status = installed === source ? "up-to-date" : "modified";
519
+ }
520
+ return { relativePath, path, status };
521
+ });
522
+ return {
523
+ name,
524
+ installed: files.some((file) => file.status !== "missing"),
525
+ complete: files.every((file) => file.status !== "missing"),
526
+ files
527
+ };
528
+ });
529
+ }
530
+ function initSkills(options = {}) {
531
+ return writeSkills({ ...options, mode: "init" });
532
+ }
533
+ function updateSkills(options = {}) {
534
+ return writeSkills({ ...options, mode: "update" });
535
+ }
536
+ function writeSkills(options) {
537
+ const skillsDirectory = options.skillsDirectory ?? bundledSkillsDirectory();
538
+ const cwd = options.cwd ?? process.cwd();
539
+ const force = options.force === true;
540
+ const canonicalDir = options.dir ?? "skills";
541
+ const targets = new Set(options.targets ?? ["anvia"]);
542
+ targets.add("anvia");
543
+ const results = [];
544
+ const anvia = syncSkillTree({
545
+ skillsDirectory,
546
+ targetRoot: skillsTargetDirectory(options),
547
+ force,
548
+ mode: options.mode
549
+ });
550
+ results.push({ target: "anvia", ...anvia });
551
+ if (targets.has("claude")) {
552
+ const claude = syncClaudeSkills({ cwd, skillsDirectory, force, mode: options.mode });
553
+ results.push({ target: "claude", ...claude });
554
+ }
555
+ if (targets.has("cursor")) {
556
+ const cursor = syncCursorRules({
557
+ skillsDirectory,
558
+ rulesDirectory: join6(cwd, ".cursor", "rules"),
559
+ canonicalDir,
560
+ force,
561
+ mode: options.mode
562
+ });
563
+ results.push({ target: "cursor", ...cursor });
564
+ }
565
+ if (targets.has("agents") || targets.has("codex")) {
566
+ const doc = syncAgentsDoc({
567
+ agentsPath: join6(cwd, "AGENTS.md"),
568
+ skillsDirectory,
569
+ canonicalDir
570
+ });
571
+ const target = targets.has("agents") ? "agents" : "codex";
572
+ results.push({ target, ...doc });
573
+ }
574
+ return {
575
+ report: inspectInstalledSkills(options),
576
+ created: anvia.created,
577
+ updated: anvia.updated,
578
+ targets: results
579
+ };
580
+ }
581
+
582
+ // src/skills/types.ts
583
+ var skillsTargetNames = ["anvia", "claude", "codex", "cursor", "agents"];
584
+ function isSkillsTarget(value) {
585
+ return skillsTargetNames.includes(value);
586
+ }
587
+
588
+ export {
589
+ registryItemNames,
590
+ createRegistryItem,
591
+ initializeProject,
592
+ addRegistryItem,
593
+ isRegistryItemName,
594
+ inspectInstalledItems,
595
+ updateInstalledItems,
596
+ closestRegistryItemName,
597
+ bundledSkillsDirectory,
598
+ skillNames,
599
+ skillsTargetDirectory,
600
+ collectSkillFiles,
601
+ inspectInstalledSkills,
602
+ initSkills,
603
+ updateSkills,
604
+ skillsTargetNames,
605
+ isSkillsTarget
606
+ };
607
+ //# sourceMappingURL=chunk-ERCP4EIZ.js.map