@xera-ai/cli 0.12.1 → 0.12.2
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/dist/index.js +91 -53
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -210,21 +210,34 @@ async function runChecks(cwd) {
|
|
|
210
210
|
checks.push({ name: "xera skills present", ok: false, message: "run `xera init`" });
|
|
211
211
|
} else {
|
|
212
212
|
const required = [
|
|
213
|
-
"xera-run
|
|
214
|
-
"xera-fetch
|
|
215
|
-
"xera-feature
|
|
216
|
-
"xera-script
|
|
217
|
-
"xera-exec
|
|
218
|
-
"xera-report
|
|
219
|
-
"xera-promote
|
|
213
|
+
"xera-run",
|
|
214
|
+
"xera-fetch",
|
|
215
|
+
"xera-feature",
|
|
216
|
+
"xera-script",
|
|
217
|
+
"xera-exec",
|
|
218
|
+
"xera-report",
|
|
219
|
+
"xera-promote"
|
|
220
220
|
];
|
|
221
|
-
const missing =
|
|
221
|
+
const missing = [];
|
|
222
|
+
const legacyFlat = [];
|
|
223
|
+
for (const base of required) {
|
|
224
|
+
if (existsSync(join(skillsDir, base, "SKILL.md")))
|
|
225
|
+
continue;
|
|
226
|
+
if (existsSync(join(skillsDir, `${base}.md`))) {
|
|
227
|
+
legacyFlat.push(base);
|
|
228
|
+
} else {
|
|
229
|
+
missing.push(base);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
222
232
|
const skillsCheck = {
|
|
223
233
|
name: "xera skills present",
|
|
224
|
-
ok: missing.length === 0
|
|
234
|
+
ok: missing.length === 0 && legacyFlat.length === 0
|
|
225
235
|
};
|
|
226
|
-
if (missing.length)
|
|
227
|
-
skillsCheck.message = `missing: ${missing.join(", ")}`;
|
|
236
|
+
if (missing.length) {
|
|
237
|
+
skillsCheck.message = `missing: ${missing.map((b) => `${b}/SKILL.md`).join(", ")}`;
|
|
238
|
+
} else if (legacyFlat.length) {
|
|
239
|
+
skillsCheck.message = `legacy flat layout in .claude/skills/ \u2014 run \`xera init --update\` to migrate to <name>/SKILL.md (Claude Code Skill tool requires the directory layout)`;
|
|
240
|
+
}
|
|
228
241
|
checks.push(skillsCheck);
|
|
229
242
|
}
|
|
230
243
|
return checks;
|
|
@@ -261,9 +274,16 @@ async function doctorCommand(opts) {
|
|
|
261
274
|
}
|
|
262
275
|
|
|
263
276
|
// src/commands/init.ts
|
|
264
|
-
import {
|
|
277
|
+
import {
|
|
278
|
+
appendFileSync,
|
|
279
|
+
existsSync as existsSync3,
|
|
280
|
+
mkdirSync as mkdirSync2,
|
|
281
|
+
readdirSync as readdirSync2,
|
|
282
|
+
readFileSync as readFileSync3,
|
|
283
|
+
writeFileSync as writeFileSync2
|
|
284
|
+
} from "fs";
|
|
265
285
|
import { createRequire } from "module";
|
|
266
|
-
import { join as join3 } from "path";
|
|
286
|
+
import { dirname as dirname2, join as join3 } from "path";
|
|
267
287
|
import * as p from "@clack/prompts";
|
|
268
288
|
import { generateKey } from "@xera-ai/core";
|
|
269
289
|
import pc2 from "picocolors";
|
|
@@ -293,17 +313,6 @@ function scaffoldFile(targetPath, templateName, vars) {
|
|
|
293
313
|
mkdirSync(dirname(targetPath), { recursive: true });
|
|
294
314
|
writeFileSync(targetPath, render(tmpl, vars));
|
|
295
315
|
}
|
|
296
|
-
function copyDir(src, dest) {
|
|
297
|
-
mkdirSync(dest, { recursive: true });
|
|
298
|
-
for (const entry of readdirSync(src, { withFileTypes: true })) {
|
|
299
|
-
const s = join2(src, entry.name);
|
|
300
|
-
const d = join2(dest, entry.name);
|
|
301
|
-
if (entry.isDirectory())
|
|
302
|
-
copyDir(s, d);
|
|
303
|
-
else
|
|
304
|
-
writeFileSync(d, readFileSync2(s));
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
316
|
|
|
308
317
|
// src/commands/init.ts
|
|
309
318
|
var require2 = createRequire(import.meta.url);
|
|
@@ -446,15 +455,22 @@ async function initCommand(opts) {
|
|
|
446
455
|
writeFileSync2(gitignorePath, `${gitignoreAdditions.trim()}
|
|
447
456
|
`);
|
|
448
457
|
}
|
|
449
|
-
const
|
|
450
|
-
const
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
+
const skillsPkgPath = require2.resolve("@xera-ai/skills/package.json");
|
|
459
|
+
const skillsSrcDir = join3(skillsPkgPath, "..");
|
|
460
|
+
const SKILL_IGNORE = new Set(["package.json", "version.json", "CHANGELOG.md"]);
|
|
461
|
+
for (const name of readdirSync2(skillsSrcDir)) {
|
|
462
|
+
if (SKILL_IGNORE.has(name))
|
|
463
|
+
continue;
|
|
464
|
+
if (!name.endsWith(".md"))
|
|
465
|
+
continue;
|
|
466
|
+
const content = readFileSync3(join3(skillsSrcDir, name));
|
|
467
|
+
const base = name.replace(/\.md$/, "");
|
|
468
|
+
const skillFile = join3(cwd, ".claude/skills", base, "SKILL.md");
|
|
469
|
+
mkdirSync2(dirname2(skillFile), { recursive: true });
|
|
470
|
+
writeFileSync2(skillFile, content);
|
|
471
|
+
const cmdFile = join3(cwd, ".claude/commands", name);
|
|
472
|
+
mkdirSync2(dirname2(cmdFile), { recursive: true });
|
|
473
|
+
writeFileSync2(cmdFile, content);
|
|
458
474
|
}
|
|
459
475
|
const pkgPath = join3(cwd, "package.json");
|
|
460
476
|
const pkg = existsSync3(pkgPath) ? JSON.parse(readFileSync3(pkgPath, "utf8")) : { name: "xera-project", private: true, type: "module" };
|
|
@@ -528,13 +544,14 @@ Next:
|
|
|
528
544
|
import {
|
|
529
545
|
copyFileSync,
|
|
530
546
|
existsSync as existsSync4,
|
|
531
|
-
mkdirSync as
|
|
532
|
-
readdirSync as
|
|
547
|
+
mkdirSync as mkdirSync3,
|
|
548
|
+
readdirSync as readdirSync3,
|
|
533
549
|
readFileSync as readFileSync4,
|
|
550
|
+
unlinkSync,
|
|
534
551
|
writeFileSync as writeFileSync3
|
|
535
552
|
} from "fs";
|
|
536
553
|
import { createRequire as createRequire2 } from "module";
|
|
537
|
-
import { dirname as
|
|
554
|
+
import { dirname as dirname3, join as join4 } from "path";
|
|
538
555
|
import * as p2 from "@clack/prompts";
|
|
539
556
|
import pc3 from "picocolors";
|
|
540
557
|
var require3 = createRequire2(import.meta.url);
|
|
@@ -647,7 +664,7 @@ async function initUpdateCommand(opts) {
|
|
|
647
664
|
pkg.scripts["xera:disputes"] = "xera-internal disputes";
|
|
648
665
|
writeFileSync3(pkgPath, JSON.stringify(pkg, null, 2));
|
|
649
666
|
const wfDir = join4(cwd, ".github/workflows");
|
|
650
|
-
|
|
667
|
+
mkdirSync3(wfDir, { recursive: true });
|
|
651
668
|
try {
|
|
652
669
|
const cliPkgPath = require3.resolve("@xera-ai/cli/package.json");
|
|
653
670
|
const cliTplPath = join4(cliPkgPath, "..", "templates/xera-graph.yml.template");
|
|
@@ -658,28 +675,47 @@ async function initUpdateCommand(opts) {
|
|
|
658
675
|
}
|
|
659
676
|
const skillsSrc = require3.resolve("@xera-ai/skills/package.json");
|
|
660
677
|
const newSkillsDir = join4(skillsSrc, "..");
|
|
661
|
-
const
|
|
662
|
-
for (const name of
|
|
678
|
+
const SKILL_IGNORE = new Set(["package.json", "version.json", "CHANGELOG.md"]);
|
|
679
|
+
for (const name of readdirSync3(newSkillsDir)) {
|
|
680
|
+
if (SKILL_IGNORE.has(name))
|
|
681
|
+
continue;
|
|
663
682
|
if (!name.endsWith(".md"))
|
|
664
683
|
continue;
|
|
665
684
|
const newContent = readFileSync4(join4(newSkillsDir, name), "utf8");
|
|
666
|
-
const
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
685
|
+
const base = name.replace(/\.md$/, "");
|
|
686
|
+
const skillPath = join4(cwd, ".claude/skills", base, "SKILL.md");
|
|
687
|
+
const legacyFlatSkillPath = join4(cwd, ".claude/skills", name);
|
|
688
|
+
const cmdPath = join4(cwd, ".claude/commands", name);
|
|
689
|
+
let migratedLegacy = false;
|
|
690
|
+
if (existsSync4(legacyFlatSkillPath) && !existsSync4(skillPath)) {
|
|
691
|
+
const legacyContent = readFileSync4(legacyFlatSkillPath, "utf8");
|
|
692
|
+
mkdirSync3(dirname3(skillPath), { recursive: true });
|
|
693
|
+
writeFileSync3(skillPath, legacyContent);
|
|
694
|
+
unlinkSync(legacyFlatSkillPath);
|
|
695
|
+
migratedLegacy = true;
|
|
696
|
+
}
|
|
697
|
+
const targets = [];
|
|
698
|
+
for (const path of [skillPath, cmdPath]) {
|
|
699
|
+
if (!existsSync4(path)) {
|
|
700
|
+
targets.push({ path, state: "missing" });
|
|
701
|
+
} else {
|
|
702
|
+
const content = readFileSync4(path, "utf8");
|
|
703
|
+
targets.push({ path, state: content === newContent ? "same" : "diff" });
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
if (targets.every((s) => s.state === "missing")) {
|
|
707
|
+
for (const { path } of targets) {
|
|
708
|
+
mkdirSync3(dirname3(path), { recursive: true });
|
|
676
709
|
writeFileSync3(path, newContent);
|
|
677
710
|
}
|
|
678
711
|
p2.log.info(`+ ${name}`);
|
|
679
712
|
continue;
|
|
680
713
|
}
|
|
681
|
-
if (
|
|
682
|
-
|
|
714
|
+
if (targets.every((s) => s.state === "same")) {
|
|
715
|
+
if (migratedLegacy)
|
|
716
|
+
p2.log.success(`migrated ${name} to .claude/skills/${base}/SKILL.md`);
|
|
717
|
+
else
|
|
718
|
+
p2.log.info(`= ${name}`);
|
|
683
719
|
continue;
|
|
684
720
|
}
|
|
685
721
|
const choice = await p2.select({
|
|
@@ -690,12 +726,14 @@ async function initUpdateCommand(opts) {
|
|
|
690
726
|
]
|
|
691
727
|
});
|
|
692
728
|
if (choice === "overwrite") {
|
|
693
|
-
for (const { path } of
|
|
694
|
-
|
|
729
|
+
for (const { path } of targets) {
|
|
730
|
+
mkdirSync3(dirname3(path), { recursive: true });
|
|
695
731
|
writeFileSync3(path, newContent);
|
|
696
732
|
}
|
|
697
733
|
p2.log.success(`overwrote ${name}`);
|
|
698
734
|
} else {
|
|
735
|
+
if (migratedLegacy)
|
|
736
|
+
p2.log.success(`migrated ${name} to .claude/skills/${base}/SKILL.md`);
|
|
699
737
|
p2.log.warn(`kept local ${name}`);
|
|
700
738
|
}
|
|
701
739
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xera-ai/cli",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"xera": "./bin/xera"
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
"typecheck": "tsc --noEmit"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@xera-ai/core": "^0.12.
|
|
19
|
-
"@xera-ai/skills": "^0.12.
|
|
18
|
+
"@xera-ai/core": "^0.12.2",
|
|
19
|
+
"@xera-ai/skills": "^0.12.2",
|
|
20
20
|
"@clack/prompts": "1.4.0",
|
|
21
21
|
"cac": "7.0.0",
|
|
22
22
|
"picocolors": "1.1.1"
|