@xera-ai/cli 0.12.2 → 0.12.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/dist/index.js +94 -2
- package/package.json +5 -4
package/dist/index.js
CHANGED
|
@@ -15,7 +15,95 @@ import pc from "picocolors";
|
|
|
15
15
|
import { existsSync, readFileSync } from "fs";
|
|
16
16
|
import { join } from "path";
|
|
17
17
|
import { loadConfig, readAuthState } from "@xera-ai/core";
|
|
18
|
-
|
|
18
|
+
import { parse as parseYaml } from "yaml";
|
|
19
|
+
function pushTicketChecks(checks, cwd, ticket, acFieldConfigured) {
|
|
20
|
+
const ticketDir = join(cwd, ".xera", ticket);
|
|
21
|
+
if (!existsSync(ticketDir)) {
|
|
22
|
+
checks.push({
|
|
23
|
+
name: `${ticket}: .xera/${ticket}/ exists`,
|
|
24
|
+
ok: false,
|
|
25
|
+
message: `no artifact dir \u2014 run \`/xera-fetch ${ticket}\` first`
|
|
26
|
+
});
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const giPath = join(ticketDir, "graph-input.json");
|
|
30
|
+
if (!existsSync(giPath)) {
|
|
31
|
+
checks.push({
|
|
32
|
+
name: `${ticket}: graph-input.json present`,
|
|
33
|
+
ok: false,
|
|
34
|
+
message: `missing \u2014 modifiesAreas will be []; run step 5 of /xera-fetch (extract-areas prompt)`
|
|
35
|
+
});
|
|
36
|
+
} else {
|
|
37
|
+
try {
|
|
38
|
+
const data = JSON.parse(readFileSync(giPath, "utf8"));
|
|
39
|
+
if (!Array.isArray(data.modifiesAreas)) {
|
|
40
|
+
checks.push({
|
|
41
|
+
name: `${ticket}: graph-input.json present`,
|
|
42
|
+
ok: false,
|
|
43
|
+
message: `parsed but modifiesAreas is not an array \u2014 re-run step 5 of /xera-fetch`
|
|
44
|
+
});
|
|
45
|
+
} else {
|
|
46
|
+
checks.push({
|
|
47
|
+
name: `${ticket}: graph-input.json present`,
|
|
48
|
+
ok: true,
|
|
49
|
+
message: `${data.modifiesAreas.length} area(s)`
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
} catch (e) {
|
|
53
|
+
checks.push({
|
|
54
|
+
name: `${ticket}: graph-input.json present`,
|
|
55
|
+
ok: false,
|
|
56
|
+
message: `invalid JSON (${e.message}) \u2014 re-run step 5 of /xera-fetch`
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
const storyPath = join(ticketDir, "story.md");
|
|
61
|
+
if (!existsSync(storyPath)) {
|
|
62
|
+
checks.push({
|
|
63
|
+
name: `${ticket}: story.md acceptanceCriteria`,
|
|
64
|
+
ok: false,
|
|
65
|
+
message: `story.md missing \u2014 re-run /xera-fetch ${ticket}`
|
|
66
|
+
});
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const raw = readFileSync(storyPath, "utf8");
|
|
70
|
+
const m = raw.match(/^---\n([\s\S]*?)\n---/);
|
|
71
|
+
if (!m) {
|
|
72
|
+
checks.push({
|
|
73
|
+
name: `${ticket}: story.md acceptanceCriteria`,
|
|
74
|
+
ok: false,
|
|
75
|
+
message: `frontmatter missing \u2014 re-run /xera-fetch ${ticket}`
|
|
76
|
+
});
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
let fm;
|
|
80
|
+
try {
|
|
81
|
+
fm = parseYaml(m[1]);
|
|
82
|
+
} catch (e) {
|
|
83
|
+
checks.push({
|
|
84
|
+
name: `${ticket}: story.md acceptanceCriteria`,
|
|
85
|
+
ok: false,
|
|
86
|
+
message: `frontmatter unparseable (${e.message})`
|
|
87
|
+
});
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const ac = Array.isArray(fm.acceptanceCriteria) ? fm.acceptanceCriteria : [];
|
|
91
|
+
if (ac.length === 0) {
|
|
92
|
+
const hint = acFieldConfigured ? `jira.fields.acceptanceCriteria is configured but Jira returned no AC for this ticket \u2014 check the ticket in Jira` : `no AC in frontmatter; AC-level coverage will be empty. Set jira.fields.acceptanceCriteria in xera.config.ts if your project stores AC in a dedicated Jira field`;
|
|
93
|
+
checks.push({
|
|
94
|
+
name: `${ticket}: story.md acceptanceCriteria`,
|
|
95
|
+
ok: false,
|
|
96
|
+
message: hint
|
|
97
|
+
});
|
|
98
|
+
} else {
|
|
99
|
+
checks.push({
|
|
100
|
+
name: `${ticket}: story.md acceptanceCriteria`,
|
|
101
|
+
ok: true,
|
|
102
|
+
message: `${ac.length} AC item(s)`
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
async function runChecks(cwd, opts = {}) {
|
|
19
107
|
const checks = [];
|
|
20
108
|
checks.push({
|
|
21
109
|
name: `bun ${process.versions.bun ?? "unknown"}`,
|
|
@@ -181,6 +269,10 @@ async function runChecks(cwd) {
|
|
|
181
269
|
}
|
|
182
270
|
} catch {}
|
|
183
271
|
}
|
|
272
|
+
if (opts.ticket) {
|
|
273
|
+
const acFieldConfigured = Boolean(cfg.jira?.fields?.acceptanceCriteria);
|
|
274
|
+
pushTicketChecks(checks, cwd, opts.ticket, acFieldConfigured);
|
|
275
|
+
}
|
|
184
276
|
} catch (e) {
|
|
185
277
|
checks.push({
|
|
186
278
|
name: "xera.config.ts found and valid",
|
|
@@ -261,7 +353,7 @@ async function doctorCommand(opts) {
|
|
|
261
353
|
console.log("Token usage estimation requires log lines with tokens_in/tokens_out fields (added by skills).");
|
|
262
354
|
return 0;
|
|
263
355
|
}
|
|
264
|
-
const checks = await runChecks(cwd);
|
|
356
|
+
const checks = await runChecks(cwd, opts.strict ? { ticket: opts.strict } : {});
|
|
265
357
|
for (const c of checks) {
|
|
266
358
|
const icon = c.ok ? pc.green("\u2713") : pc.red("\u2717");
|
|
267
359
|
console.log(`${icon} ${c.name}${c.message ? pc.dim(` \u2014 ${c.message}`) : ""}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xera-ai/cli",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"xera": "./bin/xera"
|
|
@@ -15,10 +15,11 @@
|
|
|
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.3",
|
|
19
|
+
"@xera-ai/skills": "^0.12.3",
|
|
20
20
|
"@clack/prompts": "1.4.0",
|
|
21
21
|
"cac": "7.0.0",
|
|
22
|
-
"picocolors": "1.1.1"
|
|
22
|
+
"picocolors": "1.1.1",
|
|
23
|
+
"yaml": "2.9.0"
|
|
23
24
|
}
|
|
24
25
|
}
|