@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.
Files changed (2) hide show
  1. package/dist/index.js +94 -2
  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
- async function runChecks(cwd) {
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.2",
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.2",
19
- "@xera-ai/skills": "^0.12.2",
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
  }